Skip to content

Commit 6ebbe53

Browse files
authored
refactor: Automatically assign the attributes when concatenating strings (#1026)
1 parent bc0a7d8 commit 6ebbe53

File tree

6 files changed

+343
-16
lines changed

6 files changed

+343
-16
lines changed

src/PhpParser/Node/FullyQualifiedFactory.php

+10-5
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,20 @@ final class FullyQualifiedFactory
2424
use NotInstantiable;
2525

2626
/**
27-
* @param string|string[]|Name|null $name1
28-
* @param string|string[]|Name|null $name2
27+
* @param string|Name|string[]|null $name1
28+
* @param string|Name|string[]|null $name2
2929
*/
30-
public static function concat($name1, $name2, array $attributes = []): FullyQualified
31-
{
30+
public static function concat(
31+
array|Name|string|null $name1,
32+
array|Name|string|null $name2,
33+
?array $attributes = null,
34+
): FullyQualified {
3235
if (null === $name1 && null === $name2) {
3336
throw new InvalidArgumentException('Expected one of the names to not be null');
3437
}
3538

36-
return FullyQualified::concat($name1, $name2, $attributes);
39+
$newAttributes = NameFactory::getConcatenatedNamesAttributes($name1, $name2, $attributes);
40+
41+
return FullyQualified::concat($name1, $name2, $newAttributes);
3742
}
3843
}

src/PhpParser/Node/NameFactory.php

+26-5
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,36 @@ final class NameFactory
2323
use NotInstantiable;
2424

2525
/**
26-
* @param string|string[]|Name|null $name1
27-
* @param string|string[]|Name|null $name2
26+
* @param string|Name|string[]|null $name1
27+
* @param string|Name|string[]|null $name2
2828
*/
29-
public static function concat($name1, $name2, array $attributes = []): Name
30-
{
29+
public static function concat(
30+
array|Name|string|null $name1,
31+
array|Name|string|null $name2,
32+
?array $attributes = null,
33+
): Name {
3134
if (null === $name1 && null === $name2) {
3235
throw new InvalidArgumentException('Expected one of the names to not be null');
3336
}
3437

35-
return Name::concat($name1, $name2, $attributes);
38+
$newAttributes = self::getConcatenatedNamesAttributes($name1, $name2, $attributes);
39+
40+
return Name::concat($name1, $name2, $newAttributes);
41+
}
42+
43+
/**
44+
* @param string|string[]|Name|null $name1
45+
* @param string|string[]|Name|null $name2
46+
*/
47+
public static function getConcatenatedNamesAttributes(
48+
string|array|Name|null $name1,
49+
string|array|Name|null $name2,
50+
?array $attributes = null,
51+
): array {
52+
return match (true) {
53+
$name2 instanceof Name => $attributes ?? $name2->getAttributes(),
54+
$name1 instanceof Name => $attributes ?? $name1->getAttributes(),
55+
default => $attributes ?? [],
56+
};
3657
}
3758
}

src/PhpParser/NodeTraverser.php

-1
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,6 @@ static function (UseUse $use) use ($node): Use_ {
181181
NameFactory::concat(
182182
$node->prefix,
183183
$use->name,
184-
$use->name->getAttributes(),
185184
),
186185
$use->alias,
187186
$use->type,

src/PhpParser/NodeVisitor/NameStmtPrefixer.php

+1-5
Original file line numberDiff line numberDiff line change
@@ -213,11 +213,7 @@ private function prefixName(Name $resolvedName, Node $parentNode): Node
213213
// Continue
214214
}
215215

216-
return FullyQualifiedFactory::concat(
217-
$this->prefix,
218-
$resolvedName->toString(),
219-
$resolvedName->getAttributes(),
220-
);
216+
return FullyQualifiedFactory::concat($this->prefix, $resolvedName);
221217
}
222218

223219
private static function isParentNodeSupported(Node $parentNode): bool
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the humbug/php-scoper package.
7+
*
8+
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
9+
* Pádraic Brady <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
namespace Humbug\PhpScoper\PhpParser\Node;
16+
17+
use PhpParser\Node\Name;
18+
use PhpParser\Node\Name\FullyQualified;
19+
use PHPUnit\Framework\Attributes\CoversClass;
20+
use PHPUnit\Framework\Attributes\DataProvider;
21+
use PHPUnit\Framework\TestCase;
22+
use function str_replace;
23+
24+
/**
25+
* @internal
26+
*/
27+
#[CoversClass(FullyQualifiedFactory::class)]
28+
final class FullyQualifiedFactoryTest extends TestCase
29+
{
30+
#[DataProvider('fullyQualifiedNameProvider')]
31+
public function test_it_can_concatenate_two_strings(
32+
string|array|Name|null $name1,
33+
string|array|Name|null $name2,
34+
?array $attributes,
35+
Name $expected,
36+
): void {
37+
$actual = FullyQualifiedFactory::concat(
38+
$name1,
39+
$name2,
40+
$attributes,
41+
);
42+
43+
self::assertEquals($expected, $actual);
44+
}
45+
46+
public static function fullyQualifiedNameProvider(): iterable
47+
{
48+
foreach (NameFactoryTest::nameProvider() as $label => [$name1, $name2, $attributes, $expected]) {
49+
/** @var Name $expected */
50+
$expected = self::toFullyQualified($expected);
51+
52+
yield $label => [$name1, $name2, $attributes, $expected];
53+
54+
if ($name1 instanceof Name || $name2 instanceof Name) {
55+
yield str_replace('name', 'full-qualified name', $label) => [
56+
$name1 instanceof Name
57+
? self::toFullyQualified($name1)
58+
: $name1,
59+
$name2 instanceof Name
60+
? self::toFullyQualified($name2)
61+
: $name2,
62+
$attributes,
63+
$expected,
64+
];
65+
}
66+
}
67+
}
68+
69+
private static function toFullyQualified(Name $name): FullyQualified
70+
{
71+
return new FullyQualified(
72+
$name->toString(),
73+
$name->getAttributes(),
74+
);
75+
}
76+
}
+230
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/*
6+
* This file is part of the humbug/php-scoper package.
7+
*
8+
* Copyright (c) 2017 Théo FIDRY <[email protected]>,
9+
* Pádraic Brady <[email protected]>
10+
*
11+
* For the full copyright and license information, please view the LICENSE
12+
* file that was distributed with this source code.
13+
*/
14+
15+
namespace Humbug\PhpScoper\PhpParser\Node;
16+
17+
use PhpParser\Node\Name;
18+
use PHPUnit\Framework\Attributes\CoversClass;
19+
use PHPUnit\Framework\Attributes\DataProvider;
20+
use PHPUnit\Framework\TestCase;
21+
22+
/**
23+
* @internal
24+
*/
25+
#[CoversClass(NameFactory::class)]
26+
final class NameFactoryTest extends TestCase
27+
{
28+
#[DataProvider('nameProvider')]
29+
public function test_it_can_concatenate_two_strings(
30+
string|array|Name|null $name1,
31+
string|array|Name|null $name2,
32+
?array $attributes,
33+
Name $expected,
34+
): void {
35+
$actual = NameFactory::concat(
36+
$name1,
37+
$name2,
38+
$attributes,
39+
);
40+
41+
self::assertEquals($expected, $actual);
42+
}
43+
44+
public static function nameProvider(): iterable
45+
{
46+
$attributes = ['position' => 4];
47+
$firstNameAttributes = ['token' => 3];
48+
$secondNameAttributes = ['space' => 2];
49+
50+
yield 'both strings; no attributes' => [
51+
'Prefix',
52+
'main',
53+
null,
54+
new Name(
55+
'Prefix\main',
56+
[],
57+
),
58+
];
59+
60+
yield 'both strings; with attributes' => [
61+
'Prefix',
62+
'main',
63+
$attributes,
64+
new Name(
65+
'Prefix\main',
66+
$attributes,
67+
),
68+
];
69+
70+
yield 'both array of strings; no attributes' => [
71+
['Prefix', 'Humbug'],
72+
['Acme', 'main'],
73+
null,
74+
new Name(
75+
'Prefix\Humbug\Acme\main',
76+
[],
77+
),
78+
];
79+
80+
yield 'both array of strings; with attributes' => [
81+
['Prefix', 'Humbug'],
82+
['Acme', 'main'],
83+
$attributes,
84+
new Name(
85+
'Prefix\Humbug\Acme\main',
86+
$attributes,
87+
),
88+
];
89+
90+
yield 'two names; no attributes' => [
91+
new Name('Prefix'),
92+
new Name('main'),
93+
null,
94+
new Name(
95+
'Prefix\main',
96+
[],
97+
),
98+
];
99+
100+
yield 'two names; with attributes' => [
101+
new Name('Prefix'),
102+
new Name('main'),
103+
$attributes,
104+
new Name(
105+
'Prefix\main',
106+
$attributes,
107+
),
108+
];
109+
110+
yield 'two names; both with attributes and explicit attributes: explicit attributes take over' => [
111+
new Name('Prefix', $firstNameAttributes),
112+
new Name('main', $secondNameAttributes),
113+
$attributes,
114+
new Name(
115+
'Prefix\main',
116+
$attributes,
117+
),
118+
];
119+
120+
yield 'two names; both with attributes and no explicit attributes: the second name attributes are taken' => [
121+
new Name('Prefix', $firstNameAttributes),
122+
new Name('main', $secondNameAttributes),
123+
null,
124+
new Name(
125+
'Prefix\main',
126+
$secondNameAttributes,
127+
),
128+
];
129+
130+
yield 'two names; only the first name has attributes: the second name attributes are taken' => [
131+
new Name('Prefix', $firstNameAttributes),
132+
new Name('main'),
133+
null,
134+
new Name(
135+
'Prefix\main',
136+
[],
137+
),
138+
];
139+
140+
yield 'two names; only the second name has attributes: the second name attributes are taken' => [
141+
new Name('Prefix'),
142+
new Name('main', $secondNameAttributes),
143+
null,
144+
new Name(
145+
'Prefix\main',
146+
$secondNameAttributes,
147+
),
148+
];
149+
150+
yield 'only one name; name + string; no attributes' => [
151+
new Name('Prefix'),
152+
'main',
153+
null,
154+
new Name(
155+
'Prefix\main',
156+
[],
157+
),
158+
];
159+
160+
yield 'only one name; name + string; explicit attributes' => [
161+
new Name('Prefix'),
162+
'main',
163+
$attributes,
164+
new Name(
165+
'Prefix\main',
166+
$attributes,
167+
),
168+
];
169+
170+
yield 'only one name; name with attributes + string; explicit attributes' => [
171+
new Name('Prefix', $firstNameAttributes),
172+
'main',
173+
$attributes,
174+
new Name(
175+
'Prefix\main',
176+
$attributes,
177+
),
178+
];
179+
180+
yield 'only one name; name with attributes + string; no explicit attributes' => [
181+
new Name('Prefix', $firstNameAttributes),
182+
'main',
183+
null,
184+
new Name(
185+
'Prefix\main',
186+
$firstNameAttributes,
187+
),
188+
];
189+
190+
yield 'only one name; string + name; no attributes' => [
191+
'Prefix',
192+
new Name('main'),
193+
null,
194+
new Name(
195+
'Prefix\main',
196+
[],
197+
),
198+
];
199+
200+
yield 'only one name; string + name; explicit attributes' => [
201+
'Prefix',
202+
new Name('main'),
203+
$attributes,
204+
new Name(
205+
'Prefix\main',
206+
$attributes,
207+
),
208+
];
209+
210+
yield 'only one name; string + name with attributes; explicit attributes' => [
211+
'Prefix',
212+
new Name('main', $secondNameAttributes),
213+
$attributes,
214+
new Name(
215+
'Prefix\main',
216+
$attributes,
217+
),
218+
];
219+
220+
yield 'only one name; string + name with attributes; no explicit attributes' => [
221+
'Prefix',
222+
new Name('main', $secondNameAttributes),
223+
null,
224+
new Name(
225+
'Prefix\main',
226+
$secondNameAttributes,
227+
),
228+
];
229+
}
230+
}

0 commit comments

Comments
 (0)