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

j-schumann / symfony-addons / 23257254021

18 Mar 2026 05:08PM UTC coverage: 53.674% (-0.6%) from 54.23%
23257254021

push

github

web-flow
upd: RefreshDatabaseTrait: Support SQLServer, fix MySQL/MariaDB (#29)

6 of 29 new or added lines in 4 files covered. (20.69%)

1 existing line in 1 file now uncovered.

504 of 939 relevant lines covered (53.67%)

3.45 hits per line

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

0.0
/src/Rector/NamedArgumentsFromArrayRector.php
1
<?php
2

3
declare(strict_types=1);
4

5
namespace Vrok\SymfonyAddons\Rector;
6

7
use PhpParser\Node;
8
use PhpParser\Node\Arg;
9
use PhpParser\Node\Expr\Array_;
10
use PhpParser\Node\Expr\ArrayItem;
11
use PhpParser\Node\Expr\FuncCall;
12
use PhpParser\Node\Expr\MethodCall;
13
use PhpParser\Node\Expr\StaticCall;
14
use PhpParser\Node\Identifier;
15
use PhpParser\Node\Scalar\String_;
16
use Rector\Contract\Rector\ConfigurableRectorInterface;
17
use Rector\Rector\AbstractRector;
18
use Symplify\RuleDocGenerator\ValueObject\CodeSample\ConfiguredCodeSample;
19
use Symplify\RuleDocGenerator\ValueObject\RuleDefinition;
20

21
final class NamedArgumentsFromArrayRector extends AbstractRector implements ConfigurableRectorInterface
22
{
23
    /**
24
     * @var array<string|array{string, string}>
25
     */
26
    private array $targets = [];
27

28
    public function getRuleDefinition(): RuleDefinition
29
    {
30
        return new RuleDefinition(
×
31
            'Convert associative array arguments to named arguments for specified functions/methods',
×
32
            [
×
33
                new ConfiguredCodeSample(
×
34
                    <<<'CODE_SAMPLE'
×
35
foo([
36
    'a' => $a,
37
    'b' => $b,
38
]);
39

40
MyClass::staticMethod([
41
    'x' => $x,
42
    'y' => $y,
43
]);
44

45
$obj->instanceMethod([
46
    'p' => $p,
47
    'q' => $q,
48
]);
49
CODE_SAMPLE
×
50
                    ,
×
51
                    <<<'CODE_SAMPLE'
×
52
foo(a: $a, b: $b);
53

54
MyClass::staticMethod(x: $x, y: $y);
55

56
$obj->instanceMethod(p: $p, q: $q);
57
CODE_SAMPLE
×
58
                    ,
×
59
                    [
×
60
                        'targets' => [
×
61
                            'foo',  // function call
×
62
                            ['MyClass', 'staticMethod'],  // static method call
×
63
                            ['MyClass', 'instanceMethod'],  // instance method call
×
64
                        ],
×
65
                    ]
×
66
                ),
×
67
            ]
×
68
        );
×
69
    }
70

71
    /**
72
     * @return array<class-string<Node>>
73
     */
74
    public function getNodeTypes(): array
75
    {
76
        return [FuncCall::class, MethodCall::class, StaticCall::class];
×
77
    }
78

79
    public function refactor(Node $node): ?Node
80
    {
81
        if (!$this->shouldProcessNode($node)) {
×
82
            return null;
×
83
        }
84

85
        // Check if there's exactly one argument and it's an array
86
        if (1 !== \count($node->args)) {
×
87
            return null;
×
88
        }
89

90
        $firstArg = $node->args[0];
×
91
        if (!$firstArg instanceof Arg || !$firstArg->value instanceof Array_) {
×
92
            return null;
×
93
        }
94

95
        $array = $firstArg->value;
×
96

97
        // Check if all array items have string keys (associative array)
98
        if (!$this->isAssociativeArray($array)) {
×
99
            return null;
×
100
        }
101

102
        // Convert array items to named arguments
103
        $namedArgs = $this->convertArrayItemsToNamedArgs($array);
×
104

105
        if ([] === $namedArgs) {
×
106
            return null;
×
107
        }
108

109
        $node->args = $namedArgs;
×
110

111
        return $node;
×
112
    }
113

114
    /**
115
     * @param mixed[] $configuration
116
     */
117
    public function configure(array $configuration): void
118
    {
119
        $this->targets = $configuration['targets'] ?? [];
×
120
    }
121

122
    private function shouldProcessNode(Node $node): bool
123
    {
124
        if ($node instanceof FuncCall) {
×
125
            return $this->isFunctionCallTarget($node);
×
126
        }
127

128
        if ($node instanceof MethodCall) {
×
129
            return $this->isMethodCallTarget($node);
×
130
        }
131

132
        if ($node instanceof StaticCall) {
×
133
            return $this->isStaticCallTarget($node);
×
134
        }
135

136
        return false;
×
137
    }
138

139
    private function isFunctionCallTarget(FuncCall $funcCall): bool
140
    {
141
        if (!$funcCall->name instanceof Identifier) {
×
142
            return false;
×
143
        }
144

145
        $functionName = $funcCall->name->toString();
×
146

147
        // String targets are function calls
NEW
148
        return array_any($this->targets, static fn ($target) => \is_string($target) && $target === $functionName);
×
149
    }
150

151
    private function isMethodCallTarget(MethodCall $methodCall): bool
152
    {
153
        if (!$methodCall->name instanceof Identifier) {
×
154
            return false;
×
155
        }
156

157
        $methodName = $methodCall->name->toString();
×
158

159
        foreach ($this->targets as $target) {
×
160
            if (\is_array($target) && 2 === \count($target)) {
×
161
                [, $targetMethod] = $target;
×
162

163
                if ($targetMethod === $methodName) {
×
164
                    return true;
×
165
                }
166
            }
167
        }
168

169
        return false;
×
170
    }
171

172
    private function isStaticCallTarget(StaticCall $staticCall): bool
173
    {
174
        if (!$staticCall->name instanceof Identifier) {
×
175
            return false;
×
176
        }
177

178
        $methodName = $staticCall->name->toString();
×
179

180
        $className = null;
×
181
        if ($staticCall->class instanceof Identifier) {
×
182
            $className = $staticCall->class->toString();
×
183
        }
184

185
        foreach ($this->targets as $target) {
×
186
            if (\is_array($target) && 2 === \count($target)) {
×
187
                [$targetClass, $targetMethod] = $target;
×
188

189
                if ($targetClass === $className && $targetMethod === $methodName) {
×
190
                    return true;
×
191
                }
192
            }
193
        }
194

195
        return false;
×
196
    }
197

198
    private function isAssociativeArray(Array_ $array): bool
199
    {
200
        foreach ($array->items as $item) {
×
201
            if (!$item instanceof ArrayItem) {
×
202
                continue;
×
203
            }
204

205
            if (null === $item->key) {
×
206
                return false;
×
207
            }
208

209
            if (!$item->key instanceof String_) {
×
210
                return false;
×
211
            }
212
        }
213

214
        return true;
×
215
    }
216

217
    /**
218
     * @return Arg[]
219
     */
220
    private function convertArrayItemsToNamedArgs(Array_ $array): array
221
    {
222
        $namedArgs = [];
×
223

224
        foreach ($array->items as $item) {
×
225
            if (!$item instanceof ArrayItem || !$item->key instanceof String_) {
×
226
                continue;
×
227
            }
228

229
            $paramName = $item->key->value;
×
230
            $namedArgs[] = new Arg(
×
231
                $item->value,
×
232
                false, // byRef
×
233
                false, // unpack
×
234
                [],    // attributes
×
235
                new Identifier($paramName) // name
×
236
            );
×
237
        }
238

239
        return $namedArgs;
×
240
    }
241
}
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