Skip to content

PHP 8+ @ suppressed errors cannot be distinguished anymore in many cases #18950

Open
@kkmuffme

Description

@kkmuffme

Description

https://www.php.net/manual/en/migration80.incompatible.php

The @ operator will no longer silence fatal errors (E_ERROR, E_CORE_ERROR, E_COMPILE_ERROR, E_USER_ERROR, E_RECOVERABLE_ERROR, E_PARSE). Error handlers that expect error_reporting to be 0 when @ is used, should be adjusted to use a mask check instead:

While in PHP<8 this meant error_reporting is set to 0 when @ is used, in PHP 8+ you'd expect that it's set to 4437 when @ is used - which seems to be the case, except not if the error type is excluded already by the error_reporting level OR if it's included but does not contain all of the error types*
This means, it's not possible anymore to distinguish @ suppressed and non-suppressed errors errors that would not report bc the error reporting level excludes them.

The following code:

<?php

function log_error( $type, $message, $file, $line ) {
    $error_reporting = error_reporting();
    echo  $error_reporting . ' - ' . ( $error_reporting & $type ) . PHP_EOL;
}

set_error_handler( 'log_error' );

trigger_error( 'A', E_USER_NOTICE );
@trigger_error( 'A', E_USER_NOTICE );

$constants = ['E_ERROR', 'E_USER_ERROR', 'E_USER_WARNING', 'E_USER_NOTICE', 'E_DEPRECATED', 'E_USER_DEPRECATED', 'E_RECOVERABLE_ERROR', 'E_ALL'];

foreach ( $constants as $constant ) {
    echo $constant . ': ' . constant( $constant ) . PHP_EOL;
    error_reporting( constant( $constant ) );
    trigger_error( 'A', E_USER_NOTICE );
    @trigger_error( 'A', E_USER_NOTICE );
}

Resulted in this output with PHP 8.4: https://3v4l.org/Y4rlG#v8.4.8

-1 - 1024
4437 - 0
E_ERROR: 1
1 - 0
1 - 0
E_USER_ERROR: 256
256 - 0
256 - 0
E_USER_WARNING: 512
512 - 0
0 - 0
E_USER_NOTICE: 1024
1024 - 1024
0 - 0
E_DEPRECATED: 8192
8192 - 0
0 - 0
E_USER_DEPRECATED: 16384
16384 - 0
0 - 0
E_RECOVERABLE_ERROR: 4096
4096 - 0
4096 - 0
E_ALL: 30719
30719 - 1024
4437 - 0

Resulted in this output with PHP 7.4: https://3v4l.org/Y4rlG#v7.4.33

-1 - 1024
0 - 0
E_ERROR: 1
1 - 0
0 - 0
E_USER_ERROR: 256
256 - 0
0 - 0
E_USER_WARNING: 512
512 - 0
0 - 0
E_USER_NOTICE: 1024
1024 - 1024
0 - 0
E_DEPRECATED: 8192
8192 - 0
0 - 0
E_USER_DEPRECATED: 16384
16384 - 0
0 - 0
E_RECOVERABLE_ERROR: 4096
4096 - 0
0 - 0
E_ALL: 32767
32767 - 1024
0 - 0

But I expected this output instead:
Not really sure what would make sense.

e.g.

<?php

function log_error( $type, $message, $file, $line ) {
    if ( $type === E_DEPRECATED ) {
        return true;
    }
    $error_reporting = error_reporting();
    echo $error_reporting . ' - ' . ( $error_reporting & $type ) . PHP_EOL;
    return true;
}

set_error_handler('log_error');
//error_reporting( E_ALL );
error_reporting( E_ERROR );

trigger_error( 'A', E_USER_ERROR );
@trigger_error( 'A', E_USER_ERROR );

in 7.4 https://3v4l.org/0fIc8#v7.4.33

1 - 0
0 - 0

in 8.4 https://3v4l.org/0fIc8#v8.4.8

1 - 0
1 - 0

*It's impossible to know whether it was suppressed or not:
or e.g. https://3v4l.org/MmGIp#v8.4.8

<?php

function log_error( $type, $message, $file, $line ) {
    $error_reporting = error_reporting();
    echo  $error_reporting . ' - ' . ( $error_reporting & $type ) . PHP_EOL;
}

set_error_handler( 'log_error' );

error_reporting( ( E_ERROR | E_USER_NOTICE ) );

trigger_error( 'A', E_USER_NOTICE );
@trigger_error( 'A', E_USER_NOTICE );

PHP 8.4

1025 - 1024
1 - 0

7.4

1025 - 1024
0 - 0

PHP Version

PHP 8.4

Operating System

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions