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

lmc-eu / php-coding-standard / 8703074601

16 Apr 2024 09:15AM UTC coverage: 85.083% (+0.2%) from 84.916%
8703074601

Pull #82

github

Martin Guth
fixup! Upgrade everything to its newest version, use PSR-12
Pull Request #82: Upgrade everything to it's newest version, use PSR-12, support PHP 8.3

14 of 15 new or added lines in 6 files covered. (93.33%)

6 existing lines in 1 file now uncovered.

154 of 181 relevant lines covered (85.08%)

15.97 hits per line

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

78.33
/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',
×
UNCOV
20
            [
21
                new CodeSample("<?php\n\$queryString = http_build_query(['foo' => 'bar', 'baz' => 'bat']);"),
×
UNCOV
22
            ],
UNCOV
23
            'Function `http_build_query()` uses `arg_separator.output` ini directive as default argument separator, '
UNCOV
24
            . 'however when its default value "&" is changed, query string assembled by the method will be '
UNCOV
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.',
×
NEW
27
            'Risky when other than default "&" argument separator should be used in query strings.',
×
UNCOV
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
42
    {
43
        foreach ($tokens as $index => $token) {
10✔
44
            if ($token->isGivenKind(T_STRING) && $token->getContent() === 'http_build_query') {
10✔
45
                if ($this->isFunctionCall($tokens, $index)) {
10✔
46
                    continue;
5✔
47
                }
48

49
                $this->fixFunction($tokens, $index);
10✔
50
            }
51
        }
52
    }
2✔
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
71
    {
72
        $openParenthesisIndex = $tokens->getNextTokenOfKind($functionIndex, ['(']);
10✔
73
        if ($openParenthesisIndex === null) {
10✔
74
            return;
×
75
        }
76

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

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

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

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

98
            return;
10✔
99
        }
100

101
        $tokensToInsert = [];
5✔
102

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

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

115
        $beforeCloseParenthesisIndex = $tokens->getPrevNonWhitespace($closeParenthesisIndex);
5✔
116
        $tokens->insertAt($beforeCloseParenthesisIndex + 1, $tokensToInsert);
5✔
117
    }
1✔
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
123
    {
124
        $previousIndex = $tokens->getPrevMeaningfulToken($index);
10✔
125

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

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

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

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

153
        return [];
×
154
    }
155

156
    private function setArgumentValueToAmp(Tokens $tokens, int $argumentStartIndex): void
157
    {
158
        $ampToken = new Token([T_STRING, "'&'"]);
5✔
159
        $tokens->offsetSet($argumentStartIndex, $ampToken);
5✔
160
    }
1✔
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

© 2025 Coveralls, Inc