PHPCSStandards / PHP_CodeSniffer

PHP_CodeSniffer tokenizes PHP files and detects violations of a defined set of coding standards.
BSD 3-Clause "New" or "Revised" License
880 stars 54 forks source link

PSR2/PSR12 SwitchDeclarationSniff fatal error #589

Open franss22 opened 1 month ago

franss22 commented 1 month ago

Describe the bug

SwitchDeclarationSniff throws a fatal error on some switch statements, stopping phpcbf execution.

PHP Fatal error:  Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
Stack trace:
#0 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php(146): PHP_CodeSniffer\Runner->handleErrors(8, 'Undefined index...', 'C:\\Users\\franc\\...', 146, Array)
#1 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Files\File.php(519): PHP_CodeSniffer\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff->process(Object(PHP_CodeSniffer\Files\LocalFile), 2184)
#2 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Files\LocalFile.php(92): PHP_CodeSniffer\Files\File->process()
#3 C:\Users\franc\AppData\Roaming\Compo in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php on line 623

Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
Stack trace:
#0 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php(146): PHP_CodeSniffer\Runner->handleErrors(8, 'Undefined index...', 'Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
Stack trace:
#0 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControFatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
Stack trace:
Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructuFatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\UsFatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\UsFatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
Fatal error: Uncaught PHP_CodeSniffer\Exceptions\RuntimeException: Undefined index: scope_condition in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_cers\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
res\SwitchDeclarationSniff.php on line 146 in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php:623
odesniffer\src\Runner.php:623
Stack trace:
#0 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff.php(146): PHP_CodeSniffer\Runner->handleErrors(8, 'Undefined index...', 'C:\\Users\\franc\\...', 146, Array)
#1 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Files\File.php(519): PHP_CodeSniffer\Standards\PSR2\Sniffs\ControlStructures\SwitchDeclarationSniff->process(Object(PHP_CodeSniffer\Files\LocalFile), 2184)
#2 C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Files\LocalFile.php(92): PHP_CodeSniffer\Files\File->process()
#3 C:\Users\franc\AppData\Roaming\Compo in C:\Users\franc\AppData\Roaming\Composer\vendor\squizlabs\php_codesniffer\src\Runner.php on line 623

Code sample

this is the switch statement that causes the error. Deleting this statement lets the formatter finish (but i obviously cant remove the switch as a fix to the formatter not working).

switch ($column->format) {
                        case "SELECTOR[MULTIPLE]":
                        case "SELECTOR[MULTIPLE][ADVANCED]":
                            $values = json_decode($value);

                            //Insertamos llos valores NxN
                            $success = Entity::selectMultipleNxN($column, //Datos de Columna a insertar
                                                                 $entity_id, //ID de la entidad principal insertada
                                                                 $values); //Valores a Insertar
                        break;
                        case 'SELECTOR[1XN][AVAILABLES]':
                        case 'SELECTOR[1XN][ONLYNEW]':
                        case 'SELECTOR[1XN][DOCUMENT]':
                            //consulta la disponibilidad el recurso

                            $allow = Entity::availableResources($column->entity_type_fk,$column->col_fk_1_n,json_decode($value));
                            if (!$allow) {
                                $delete = Entity::deleteByFormat(strtoupper($column->format), //Formato
                                                                 $column->entity_type_fk,     //Tabla
                                                                 $column->col_fk_1_n,         //Columna
                                                                 $entity_id,                  //Id del registro de la Entidad
                                                                 json_decode($value));        //Arreglo de Ids de entidad foranea
                                $success = false;
                                if ($delete) {
                                    $success = true;
                                }
                                break;
                            }

                        case "SELECTOR[1XN]":
                        case "SELECTOR[1XN][ALL]":

                            $delete = Entity::deleteByFormat(strtoupper($column->format),
                                                             $column->entity_type_fk,
                                                             $column->col_fk_1_n,
                                                             $entity_id,
                                                             json_decode($value));

                            $data = [$column->col_fk_1_n => $entity_id];

                            $success = Entity::updateEntity($column->entity_type_fk,
                                                            json_decode($value), 
                                                            $data); 
                            if ($success) {
                                Entity::toEntity(json_decode($value),$data,$column->entity_type_fk);
                            }

                            if ($delete || $success) {
                                $success = true;
                            }

                        break;
                        case 'PASSWORD':{

                            if (strlen($value)<8) {
                                $name = $column->name;
                                // Hacemos rollback de la transaccion
                                DB::rollBack();
                                abort(response()->json( [
                                                            'success' => false,
                                                            'errors'  => [$column->id => "El campo $name debe contener minimo 8 caracteres"],
                                                            'message' => 'Por favor inserte una Contraseña más larga'
                                                        ],400));
                            }

                            //Si el formato es tipo PASSWORD le hacemos encriptamos el valor
                            $value = Hash::make($value);
                        }
                        default:
                            // Actualizamos las nuevas relaciones
                            $success = Entity::updateEntity($entity_type->table_name,$entity_id, [$column->col_name => $value], $entity_type);
                            if ($success) {
                                $data_for_after = [$column->col_name => $value];
                                Entity::toEntity($entity_id,[$column->col_name => $value],$entity_type->table_name);
                            }
                        break;
                    }

To reproduce

Steps to reproduce the behavior:

  1. Create a file called test.php with the code sample above.
  2. Run phpcbf --standard=PSR12 test.php

Expected behavior

phpcbf should fix the switch statement sniff without crashing

Versions (please complete the following information)

Operating System Windows 11
PHP version 7.4.33
PHP_CodeSniffer version 3.10.2
Standard PSR12
Install type composer global

Please confirm

rodrigoprimo commented 1 month ago

I was able to reproduce this error. Here is a simplified code snippet that causes the same problem:

switch ($value) {
    case 'one':{
        doSomething();
    }
    default:
        return 'default';
}

I haven't investigated deeply, but on a quick look, it seems that the curly brackets and the lack of return or break in the case statement are somehow tripping the tokenizer, and the T_RETURN token is missing its scope information.

jrfnl commented 1 month ago

I believe this may be the same issue as previously reported in https://github.com/squizlabs/PHP_CodeSniffer/issues/3794, which is still open, but does contain an analysis of the underlying issues.