Skip to content

Commit a5f45b6

Browse files
authored
Merge pull request #69 from markjaquith/issue/67/nested-namespaces/fix
Limit replacements to the beginning of the namespace string
2 parents f7786bb + aca3647 commit a5f45b6

File tree

2 files changed

+72
-27
lines changed

2 files changed

+72
-27
lines changed

src/Replace/NamespaceReplacer.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,20 @@ class NamespaceReplacer extends BaseReplacer
99

1010
public function replace($contents)
1111
{
12-
$searchNamespace = addslashes($this->autoloader->getSearchNamespace());
13-
$dependencyNamespace = addslashes($this->dep_namespace);
12+
$searchNamespace = preg_quote($this->autoloader->getSearchNamespace(), '/');
13+
$dependencyNamespace = preg_quote($this->dep_namespace, '/');
1414

1515
return preg_replace_callback(
16-
'/([^a-zA-Z0-9_\x7f-\xff])((?<!' . $dependencyNamespace . ')' . $searchNamespace . '[\\\|;])/U',
16+
"
17+
/ # Start the pattern
18+
([^a-zA-Z0-9_\x7f-\xff]) # Match the non-class character before the namespace
19+
( # Start the namespace matcher
20+
(?<!$dependencyNamespace) # Does NOT start with the prefix
21+
(?<![a-zA-Z0-9_]\\\\) # Not a class-allowed character followed by a slash
22+
$searchNamespace # The namespace we're looking for
23+
[\\\|;] # Backslash, semicolon, or pipe
24+
) # End the namespace matcher
25+
/Ux",
1726
function ($matches) {
1827
return $matches[1] . $this->dep_namespace . $matches[2];
1928
},

tests/replacers/NamespaceReplacerTest.php

Lines changed: 60 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7,65 +7,101 @@
77

88
class NamespaceReplacerTest extends TestCase
99
{
10+
const PREFIX = 'My\\Mozart\\Prefix\\';
11+
1012
/** @var NamespaceReplacer */
1113
public $replacer;
1214

1315
protected function setUp(): void
1416
{
15-
$autoloader = new Psr0();
16-
$autoloader->namespace = 'Test\\Test';
17+
$this->replacer = self::createReplacer('Test\\Test');
18+
}
1719

20+
/**
21+
* Creates a NamespaceReplacer, given a namespace and a prefix.
22+
*
23+
* @param string $namespace
24+
* @param string $prefix
25+
*
26+
* @return Psr0
27+
*/
28+
protected static function createReplacer(string $namespace, string $prefix = self::PREFIX) : NamespaceReplacer
29+
{
30+
$autoloader = new Psr0;
31+
$autoloader->namespace = $namespace;
1832
$replacer = new NamespaceReplacer();
1933
$replacer->setAutoloader($autoloader);
20-
$replacer->dep_namespace = 'Prefix\\';
21-
$this->replacer = $replacer;
34+
$replacer->dep_namespace = $prefix;
35+
36+
return $replacer;
2237
}
2338

2439
/** @test */
2540
public function it_replaces_namespace_declarations(): void
2641
{
2742
$contents = 'namespace Test\\Test;';
2843
$contents = $this->replacer->replace($contents);
29-
$this->assertEquals('namespace Prefix\\Test\\Test;', $contents);
44+
45+
$this->assertEquals('namespace My\\Mozart\\Prefix\\Test\\Test;', $contents);
3046
}
3147

3248

3349
/** @test */
3450
public function it_doesnt_replaces_namespace_inside_namespace(): void
3551
{
52+
$replacer = self::createReplacer('Test');
3653

37-
$autoloader = new Psr0();
38-
$autoloader->namespace = 'Test';
39-
40-
$replacer = new NamespaceReplacer();
41-
$replacer->setAutoloader($autoloader);
42-
$replacer->dep_namespace = 'Prefix\\';
43-
$this->replacer = $replacer;
54+
$contents = "namespace Test\\Something;\n\nuse Test\\Test;";
55+
$contents = $replacer->replace($contents);
4456

45-
$contents ="namespace Test\\Something;\n\nuse Test\\Test;";
46-
$contents = $this->replacer->replace($contents);
47-
$this->assertEquals("namespace Prefix\\Test\\Something;\n\nuse Prefix\\Test\\Test;", $contents);
57+
$this->assertEquals("namespace My\\Mozart\\Prefix\\Test\\Something;\n\nuse My\\Mozart\\Prefix\\Test\\Test;", $contents);
4858
}
4959

5060
/** @test */
5161
public function it_replaces_partial_namespace_declarations(): void
5262
{
5363
$contents = 'namespace Test\\Test\\Another;';
5464
$contents = $this->replacer->replace($contents);
55-
$this->assertEquals('namespace Prefix\\Test\\Test\\Another;', $contents);
65+
66+
$this->assertEquals('namespace My\\Mozart\\Prefix\\Test\\Test\\Another;', $contents);
5667
}
5768

58-
/** @test */
69+
/** @test */
5970
public function it_doesnt_prefix_already_prefixed_namespace(): void
6071
{
61-
$autoloader = new Psr0();
62-
$autoloader->namespace = 'Test\\Another';
72+
$replacer = self::createReplacer('Test\\Another');
6373

64-
$replacer = new NamespaceReplacer();
65-
$replacer->setAutoloader($autoloader);
74+
$contents = 'namespace My\\Mozart\\Prefix\\Test\\Another;';
75+
$contents = $replacer->replace($contents);
6676

67-
$contents = 'namespace Prefix\\Test\\Another;';
68-
$contents = $this->replacer->replace($contents);
69-
$this->assertEquals('namespace Prefix\\Test\\Another;', $contents);
77+
$this->assertEquals('namespace My\\Mozart\\Prefix\\Test\\Another;', $contents);
78+
}
79+
80+
/** @test */
81+
public function it_doesnt_double_replace_namespaces_that_also_exist_inside_another_namespace(): void
82+
{
83+
$chickenReplacer = self::createReplacer('Chicken');
84+
$eggReplacer = self::createReplacer('Egg');
85+
86+
// This is a tricky situation. We are referencing Chicken\Egg,
87+
// but Egg *also* exists as a separate top level class.
88+
$contents = 'use Chicken\\Egg;';
89+
$expected = 'use My\\Mozart\\Prefix\\Chicken\\Egg;';
90+
91+
// First, we test that eggReplacer(chickenReplacer()) yields the expected result.
92+
$this->assertEquals($expected, $eggReplacer->replace($chickenReplacer->replace($contents)));
93+
94+
// Then, we test that chickenReplacer(eggReplacer()) yields the expected result.
95+
$this->assertEquals($expected, $chickenReplacer->replace($eggReplacer->replace($contents)));
96+
97+
// Now we do the same thing, but with root-relative references.
98+
$contents = 'use \\Chicken\\Egg;';
99+
$expected = 'use \\My\\Mozart\\Prefix\\Chicken\\Egg;';
100+
101+
// First, we test that eggReplacer(chickenReplacer()) yields the expected result.
102+
$this->assertEquals($expected, $eggReplacer->replace($chickenReplacer->replace($contents)));
103+
104+
// Then, we test that chickenReplacer(eggReplacer()) yields the expected result.
105+
$this->assertEquals($expected, $chickenReplacer->replace($eggReplacer->replace($contents)));
70106
}
71107
}

0 commit comments

Comments
 (0)