slevomat / coding-standard

Slevomat Coding Standard for PHP_CodeSniffer provides many useful sniffs
MIT License
1.37k stars 170 forks source link

[Feature] Consistent foreach style #1638

Open dereuromark opened 7 months ago

dereuromark commented 7 months ago

I see code like

foreach ($usecases as $usecase) :
    $usecase->setDirty('name', true);
    $usecasesTable->save($usecase);
endforeach;

in some of the projects.

I would like to auto fix this via sniffer to the cleaner version

foreach ($usecases as $usecase) {
    $usecase->setDirty('name', true);
    $usecasesTable->save($usecase);
}

Especially since the former cannot be properly used by modern IDEs as PHPStorm. Even latest version doesnt show the closing part to the opening.

Is there an easy way to add this without accidentally killing the code or levels of nesting? I checked existing sniffers and didnt find a matching one so far.

I wonder which other tokens might also be of the same type we can replace:

        T_ENDIF               => T_ENDIF,
        T_ENDFOR              => T_ENDFOR,
        T_ENDFOREACH          => T_ENDFOREACH,
        T_ENDWHILE            => T_ENDWHILE,
        T_ENDSWITCH           => T_ENDSWITCH,
        T_ENDDECLARE          => T_ENDDECLARE,
dereuromark commented 7 months ago

I analyzed the phpcs tokens

<?php
        foreach ($usecases as $usecase) :
// T_WHITESPACE (216) code=392, line=42, column=1, length=8, level=2, conditions={"65":333,"199":310}, content=`        `
// T_FOREACH (217) code=297, line=42, column=9, length=7, parenthesis_opener=219, parenthesis_closer=225, parenthesis_owner=217, scope_condition=217, scope_opener=227, scope_closer=251, level=2, conditions={"65":333,"199":310}, content=`foreach`
// T_WHITESPACE (218) code=392, line=42, column=16, length=1, level=2, conditions={"65":333,"199":310}, content=` `
// T_OPEN_PARENTHESIS (219) code=PHPCS_T_OPEN_PARENTHESIS, line=42, column=17, length=1, parenthesis_opener=219, parenthesis_owner=217, parenthesis_closer=225, level=2, conditions={"65":333,"199":310}, content=`(`
// T_VARIABLE (220) code=266, line=42, column=18, length=9, nested_parenthesis={"219":225}, level=2, conditions={"65":333,"199":310}, content=`$usecases`
// T_WHITESPACE (221) code=392, line=42, column=27, length=1, nested_parenthesis={"219":225}, level=2, conditions={"65":333,"199":310}, content=` `
// T_AS (222) code=301, line=42, column=28, length=2, nested_parenthesis={"219":225}, level=2, conditions={"65":333,"199":310}, content=`as`
// T_WHITESPACE (223) code=392, line=42, column=30, length=1, nested_parenthesis={"219":225}, level=2, conditions={"65":333,"199":310}, content=` `
// T_VARIABLE (224) code=266, line=42, column=31, length=8, nested_parenthesis={"219":225}, level=2, conditions={"65":333,"199":310}, content=`$usecase`
// T_CLOSE_PARENTHESIS (225) code=PHPCS_T_CLOSE_PARENTHESIS, line=42, column=39, length=1, parenthesis_owner=217, parenthesis_opener=219, parenthesis_closer=225, level=2, conditions={"65":333,"199":310}, content=`)`
// T_WHITESPACE (226) code=392, line=42, column=40, length=1, level=2, conditions={"65":333,"199":310}, content=` `
// T_COLON (227) code=PHPCS_T_COLON, line=42, column=41, length=1, scope_condition=217, scope_opener=227, scope_closer=251, level=2, conditions={"65":333,"199":310}, content=`:`
// T_WHITESPACE (228) code=392, line=42, column=42, length=0, level=3, conditions={"65":333,"199":310,"217":297}, content=`\n`
            $usecase->setDirty('name', true);
// T_WHITESPACE (229) code=392, line=43, column=1, length=12, level=3, conditions={"65":333,"199":310,"217":297}, content=`            `
// T_VARIABLE (230) code=266, line=43, column=13, length=8, level=3, conditions={"65":333,"199":310,"217":297}, content=`$usecase`
// T_OBJECT_OPERATOR (231) code=384, line=43, column=21, length=2, level=3, conditions={"65":333,"199":310,"217":297}, content=`->`
// T_STRING (232) code=262, line=43, column=23, length=8, level=3, conditions={"65":333,"199":310,"217":297}, content=`setDirty`
// T_OPEN_PARENTHESIS (233) code=PHPCS_T_OPEN_PARENTHESIS, line=43, column=31, length=1, parenthesis_opener=233, parenthesis_closer=238, level=3, conditions={"65":333,"199":310,"217":297}, content=`(`
// T_CONSTANT_ENCAPSED_STRING (234) code=269, line=43, column=32, length=6, nested_parenthesis={"233":238}, level=3, conditions={"65":333,"199":310,"217":297}, content=`'name'`
// T_COMMA (235) code=PHPCS_T_COMMA, line=43, column=38, length=1, nested_parenthesis={"233":238}, level=3, conditions={"65":333,"199":310,"217":297}, content=`,`
// T_WHITESPACE (236) code=392, line=43, column=39, length=1, nested_parenthesis={"233":238}, level=3, conditions={"65":333,"199":310,"217":297}, content=` `
// T_TRUE (237) code=PHPCS_T_TRUE, line=43, column=40, length=4, nested_parenthesis={"233":238}, level=3, conditions={"65":333,"199":310,"217":297}, content=`true`
// T_CLOSE_PARENTHESIS (238) code=PHPCS_T_CLOSE_PARENTHESIS, line=43, column=44, length=1, parenthesis_opener=233, parenthesis_closer=238, level=3, conditions={"65":333,"199":310,"217":297}, content=`)`
// T_SEMICOLON (239) code=PHPCS_T_SEMICOLON, line=43, column=45, length=1, level=3, conditions={"65":333,"199":310,"217":297}, content=`;`
// T_WHITESPACE (240) code=392, line=43, column=46, length=0, level=3, conditions={"65":333,"199":310,"217":297}, content=`\n`
            $usecasesTable->save($usecase);
// T_WHITESPACE (241) code=392, line=44, column=1, length=12, level=3, conditions={"65":333,"199":310,"217":297}, content=`            `
// T_VARIABLE (242) code=266, line=44, column=13, length=14, level=3, conditions={"65":333,"199":310,"217":297}, content=`$usecasesTable`
// T_OBJECT_OPERATOR (243) code=384, line=44, column=27, length=2, level=3, conditions={"65":333,"199":310,"217":297}, content=`->`
// T_STRING (244) code=262, line=44, column=29, length=4, level=3, conditions={"65":333,"199":310,"217":297}, content=`save`
// T_OPEN_PARENTHESIS (245) code=PHPCS_T_OPEN_PARENTHESIS, line=44, column=33, length=1, parenthesis_opener=245, parenthesis_closer=247, level=3, conditions={"65":333,"199":310,"217":297}, content=`(`
// T_VARIABLE (246) code=266, line=44, column=34, length=8, nested_parenthesis={"245":247}, level=3, conditions={"65":333,"199":310,"217":297}, content=`$usecase`
// T_CLOSE_PARENTHESIS (247) code=PHPCS_T_CLOSE_PARENTHESIS, line=44, column=42, length=1, parenthesis_opener=245, parenthesis_closer=247, level=3, conditions={"65":333,"199":310,"217":297}, content=`)`
// T_SEMICOLON (248) code=PHPCS_T_SEMICOLON, line=44, column=43, length=1, level=3, conditions={"65":333,"199":310,"217":297}, content=`;`
// T_WHITESPACE (249) code=392, line=44, column=44, length=0, level=3, conditions={"65":333,"199":310,"217":297}, content=`\n`
        endforeach;
// T_WHITESPACE (250) code=392, line=45, column=1, length=8, level=3, conditions={"65":333,"199":310,"217":297}, content=`        `
// T_ENDFOREACH (251) code=298, line=45, column=9, length=10, scope_condition=217, scope_opener=227, scope_closer=251, level=2, conditions={"65":333,"199":310}, content=`endforeach`
// T_SEMICOLON (252) code=PHPCS_T_SEMICOLON, line=45, column=19, length=1, level=2, conditions={"65":333,"199":310}, content=`;`
// T_WHITESPACE (253) code=392, line=45, column=20, length=0, level=2, conditions={"65":333,"199":310}, content=`\n`

It seems we just have to sniff for the T_ENDFOREACH, go to the scope_opener index and replace both. Seems like it should work safely.

Any interest in a sniff for this in the community or maintainer group?

dereuromark commented 7 months ago

I whipped sth up that seems to work fine already: https://github.com/php-collective/code-sniffer/pull/6