• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

lmc-eu / php-coding-standard / 13558276316

03 Jan 2025 07:16PM UTC coverage: 81.481%. Remained the same
13558276316

push

github

OndraM
Update PHP CS Fixer to fixed version

176 of 216 relevant lines covered (81.48%)

19.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

66.22
/src/Fixer/SpecifyArgSeparatorFixer.php
1
<?php declare(strict_types=1);
2

3
namespace Lmc\CodingStandard\Fixer;
4

5
use PhpCsFixer\Fixer\FixerInterface;
6
use PhpCsFixer\FixerDefinition\CodeSample;
7
use PhpCsFixer\FixerDefinition\FixerDefinition;
8
use PhpCsFixer\FixerDefinition\FixerDefinitionInterface;
9
use PhpCsFixer\Tokenizer\Analyzer\ArgumentsAnalyzer;
10
use PhpCsFixer\Tokenizer\CT;
11
use PhpCsFixer\Tokenizer\Token;
12
use PhpCsFixer\Tokenizer\Tokens;
13

14
class SpecifyArgSeparatorFixer implements FixerInterface
15
{
16
    public function getDefinition(): FixerDefinitionInterface
×
17
    {
18
        return new FixerDefinition(
×
19
            'Function `http_build_query()` should always have specified `$arg_separator` argument',
×
20
            [
×
21
                new CodeSample("<?php\n\$queryString = http_build_query(['foo' => 'bar', 'baz' => 'bat']);"),
×
22
            ],
×
23
            'Function `http_build_query()` uses `arg_separator.output` ini directive as default argument separator, '
×
24
            . 'however when its default value "&" is changed, query string assembled by the method will be '
×
25
            . 'unexpectedly invalid. This Fixer forces you to not rely on ini settings and rather define '
×
26
            . '`$arg_separator` in third argument of the function.',
×
27
            'Risky when other than default "&" argument separator should be used in query strings.',
×
28
        );
×
29
    }
30

31
    public function isCandidate(Tokens $tokens): bool
×
32
    {
33
        return $tokens->isTokenKindFound(T_STRING);
×
34
    }
35

36
    public function isRisky(): bool
×
37
    {
38
        return true;
×
39
    }
40

41
    public function fix(\SplFileInfo $file, Tokens $tokens): void
12✔
42
    {
43
        foreach ($tokens as $index => $token) {
12✔
44
            if ($token->isGivenKind(T_STRING) && $token->getContent() === 'http_build_query') {
12✔
45
                if ($this->isFunctionCall($tokens, $index)) {
12✔
46
                    continue;
6✔
47
                }
48

49
                $this->fixFunction($tokens, $index);
12✔
50
            }
51
        }
52
    }
53

54
    public function getName(): string
×
55
    {
56
        return self::class;
×
57
    }
58

59
    public function getPriority(): int
×
60
    {
61
        // Should be run after SingleQuoteFixer (priority 0) and ArraySyntaxFixer (priority 1)
62
        return -1;
×
63
    }
64

65
    public function supports(\SplFileInfo $file): bool
×
66
    {
67
        return true;
×
68
    }
69

70
    private function fixFunction(Tokens $tokens, int $functionIndex): void
12✔
71
    {
72
        $openParenthesisIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
12✔
73
        if ($openParenthesisIndex === null) {
12✔
74
            return;
×
75
        }
76

77
        $closeParenthesisIndex = $tokens->findBlockEnd(Tokens::BLOCK_TYPE_PARENTHESIS_BRACE, $openParenthesisIndex);
12✔
78

79
        $argumentCount = (new ArgumentsAnalyzer())
12✔
80
            ->countArguments($tokens, $openParenthesisIndex, $closeParenthesisIndex);
12✔
81

82
        // When third argument is already present and it is null, override its value.
83
        if ($argumentCount >= 3) {
12✔
84
            $thirdArgumentTuple = $this->getThirdArgumentTokenTuple(
12✔
85
                $tokens,
12✔
86
                $openParenthesisIndex,
12✔
87
                $closeParenthesisIndex,
12✔
88
            );
12✔
89
            if ($thirdArgumentTuple === []) {
12✔
90
                return;
×
91
            }
92

93
            $thirdArgumentToken = reset($thirdArgumentTuple);
12✔
94
            if ($thirdArgumentToken->getContent() === 'null') {
12✔
95
                $this->setArgumentValueToAmp($tokens, key($thirdArgumentTuple));
6✔
96
            }
97

98
            return;
12✔
99
        }
100

101
        $tokensToInsert = [];
6✔
102

103
        // Add second argument if not defined
104
        if ($argumentCount === 1) {
6✔
105
            $tokensToInsert[] = new Token(',');
6✔
106
            $tokensToInsert[] = new Token([T_WHITESPACE, ' ']);
6✔
107
            $tokensToInsert[] = new Token([T_STRING, "''"]);
6✔
108
        }
109

110
        // Add third argument (arg separator): ", '&'"
111
        $tokensToInsert[] = new Token(',');
6✔
112
        $tokensToInsert[] = new Token([T_WHITESPACE, ' ']);
6✔
113
        $tokensToInsert[] = new Token([T_STRING, "'&'"]);
6✔
114

115
        $beforeCloseParenthesisIndex = $tokens->getPrevNonWhitespace($closeParenthesisIndex);
6✔
116
        $tokens->insertAt($beforeCloseParenthesisIndex + 1, $tokensToInsert);
6✔
117
    }
118

119
    /**
120
     * Detect if this is most probably function call (and not function import or function definition).
121
     */
122
    private function isFunctionCall(Tokens $tokens, int $index): bool
12✔
123
    {
124
        $previousIndex = $tokens->getPrevMeaningfulToken($index);
12✔
125

126
        return $previousIndex !== null
12✔
127
            && ($tokens[$previousIndex]->isGivenKind(CT::T_FUNCTION_IMPORT)
12✔
128
                || $tokens[$previousIndex]->isGivenKind(T_FUNCTION));
12✔
129
    }
130

131
    /**
132
     * @return array<int, Token>
133
     */
134
    private function getThirdArgumentTokenTuple(
12✔
135
        Tokens $tokens,
136
        int $openParenthesisIndex,
137
        int $closeParenthesisIndex,
138
    ): array {
139
        $argumentsAnalyzer = new ArgumentsAnalyzer();
12✔
140
        $allArguments = $argumentsAnalyzer->getArguments($tokens, $openParenthesisIndex, $closeParenthesisIndex);
12✔
141
        $thirdArgumentIndex = array_slice($allArguments, 2, 1, true);
12✔
142

143
        for ($index = key($thirdArgumentIndex); $index <= reset($thirdArgumentIndex); $index++) {
12✔
144
            /** @var int $index */
145
            /** @var Token $token */
146
            $token = $tokens[$index];
12✔
147

148
            if (!$token->isWhitespace() && !$token->isComment()) {
12✔
149
                return [$index => $token];
12✔
150
            }
151
        }
152

153
        return [];
×
154
    }
155

156
    private function setArgumentValueToAmp(Tokens $tokens, int $argumentStartIndex): void
6✔
157
    {
158
        $ampToken = new Token([T_STRING, "'&'"]);
6✔
159
        $tokens->offsetSet($argumentStartIndex, $ampToken);
6✔
160
    }
161
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc