Skip to content

Commit

Permalink
Reorder replacements so substrings are always ahead of what they migh…
Browse files Browse the repository at this point in the history
…t be substrings of.
  • Loading branch information
BrianHenryIE committed Oct 9, 2022
1 parent 81bdb99 commit 08ba84f
Show file tree
Hide file tree
Showing 3 changed files with 241 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/Prefixer.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@ public function replaceInFiles(array $namespaceChanges, array $classChanges, arr
public function replaceInString(array $namespacesChanges, array $classes, array $originalConstants, string $contents): string
{

// Reorder this so substrings are always ahead of what they might be substrings of.
asort($namespacesChanges);
foreach ($namespacesChanges as $originalNamespace => $replacement) {
if (in_array($originalNamespace, $this->excludeNamespacesFromPrefixing)) {
continue;
Expand Down
213 changes: 213 additions & 0 deletions tests/Issues/StraussIssue47Test.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,213 @@
<?php
/**
* When the namespace being replaced is a substring of the prefix, the order of replacements
* is important, otherwise the replacement is performed twice.
*
* @see \BrianHenryIE\Strauss\Prefixer::replaceInString()
* @see asort()
*
* @see https://core.trac.wordpress.org/ticket/42670
*/

namespace BrianHenryIE\Strauss\Tests\Issues;

use BrianHenryIE\Strauss\Composer\Extra\StraussConfig;
use BrianHenryIE\Strauss\Console\Commands\Compose;
use BrianHenryIE\Strauss\Prefixer;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
* @package BrianHenryIE\Strauss\Tests\Issues
* @coversNothing
*/
class StraussIssue47Test extends \BrianHenryIE\Strauss\Tests\Integration\Util\IntegrationTestCase
{

/*
* The proper failing test.
*/
public function test_double_namespace()
{

$composerJsonString = <<<'EOD'
{
"name": "brianhenryie/double-namespace-47",
"minimum-stability": "dev",
"repositories": {
"dragon-public/framework": {
"type": "git",
"url": "https://gitlab.com/dragon-public/framework/"
}
},
"require": {
"dragon-public/framework": "*"
},
"extra": {
"strauss": {
"namespace_prefix": "Dragon\\Dependencies\\",
"target_directory": "/strauss/",
"classmap_prefix": "Dragon_Dependencies_"
}
}
}

EOD;

file_put_contents($this->testsWorkingDir . 'composer.json', $composerJsonString);

chdir($this->testsWorkingDir);

exec('composer install');

$inputInterfaceMock = $this->createMock(InputInterface::class);
$outputInterfaceMock = $this->createMock(OutputInterface::class);

$strauss = new Compose();

$result = $strauss->run($inputInterfaceMock, $outputInterfaceMock);

// 0 for no errors.
$this->assertNotEquals(1, $result);

$php_string = file_get_contents($this->testsWorkingDir . 'strauss/dragon-public/framework/src/Form/TextArea.php');

$this->assertStringNotContainsString('namespace Dragon\Dependencies\Dragon\Dependencies\Dragon\Form;', $php_string);
$this->assertStringContainsString('namespace Dragon\Dependencies\Dragon\Form;', $php_string);
}

/*
* Exclude all other packages, so step debugging has less noise.
*/
public function test_double_namespace_dont_copy_dependencies()
{

$composerJsonString = <<<'EOD'
{
"name": "brianhenryie/double-namespace-47",
"minimum-stability": "dev",
"repositories": {
"dragon-public/framework": {
"type": "git",
"url": "https://gitlab.com/dragon-public/framework/"
}
},
"require": {
"dragon-public/framework": "*"
},
"extra": {
"strauss": {
"namespace_prefix": "Dragon\\Dependencies\\",
"target_directory": "/strauss/",
"classmap_prefix": "Dragon_Dependencies_",
"exclude_from_copy": {
"packages": [
"guzzlehttp/guzzle",
"ramsey/uuid",
"illuminate/database",
"illuminate/filesystem",
"illuminate/translation",
"illuminate/validation",
"illuminate/pagination",
"symfony/var-dumper",
"doctrine/dbal"
]
},
"exclude_from_prefix": {
"namespaces": [
"voku\\",
"Symfony\\",
"Ramsey\\",
"Illuminate\\",
"GuzzleHttp\\",
"Egulias\\",
"Doctrine\\",
"Carbon",
"Brick\\"
]
}
}
}
}

EOD;

file_put_contents($this->testsWorkingDir . 'composer.json', $composerJsonString);

chdir($this->testsWorkingDir);

exec('composer install');

$inputInterfaceMock = $this->createMock(InputInterface::class);
$outputInterfaceMock = $this->createMock(OutputInterface::class);

$strauss = new Compose();

$result = $strauss->run($inputInterfaceMock, $outputInterfaceMock);

// 0 for no errors.
$this->assertNotEquals(1, $result);

$php_string = file_get_contents($this->testsWorkingDir . 'strauss/dragon-public/framework/src/Form/TextArea.php');

$this->assertStringNotContainsString('namespace Dragon\Dependencies\Dragon\Dependencies\Dragon\Form;', $php_string);
$this->assertStringContainsString('namespace Dragon\Dependencies\Dragon\Form;', $php_string);
}

/**
* Test only one file. This did not fail.
*/
public function test_double_namespace_only_file_copied()
{

$composerJsonString = <<<'EOD'
{
"name": "brianhenryie/double-namespace-47",
"minimum-stability": "dev",
"repositories": {
"dragon-public/framework": {
"type": "git",
"url": "https://gitlab.com/dragon-public/framework/"
}
},
"require": {
"dragon-public/framework": "*"
},
"extra": {
"strauss": {
"namespace_prefix": "Dragon\\Dependencies\\",
"target_directory": "/strauss/",
"classmap_prefix": "Dragon_Dependencies_",
"exclude_from_copy": {
"file_patterns": [
"/^((?!Form\\/TextArea.php$).)*$/"
]
}
}
}
}

EOD;

file_put_contents($this->testsWorkingDir . 'composer.json', $composerJsonString);

chdir($this->testsWorkingDir);

exec('composer install');

$inputInterfaceMock = $this->createMock(InputInterface::class);
$outputInterfaceMock = $this->createMock(OutputInterface::class);

$strauss = new Compose();

$result = $strauss->run($inputInterfaceMock, $outputInterfaceMock);

// 0 for no errors.
$this->assertNotEquals(1, $result);

$php_string = file_get_contents($this->testsWorkingDir . 'strauss/dragon-public/framework/src/Form/TextArea.php');

$this->assertStringNotContainsString('namespace Dragon\Dependencies\Dragon\Dependencies\Dragon\Form;', $php_string);
$this->assertStringContainsString('namespace Dragon\Dependencies\Dragon\Form;', $php_string);
}
}
26 changes: 26 additions & 0 deletions tests/Unit/PrefixerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,32 @@ public function test_it_doesnt_prefix_already_prefixed_namespace(): void
$this->assertEquals('namespace My\\Mozart\\Prefix\\Test\\Another;', $result);
}

/**
* Trying to prefix standard namespace `Dragon`, e.g. `Dragon\Form` with `Dragon\Dependencies` results in
* `Dragon\Dependencies\Dragon\Dependencies\Dragon\Form`.
*
* This was not the cause of the issue (i.e. this test, pretty much identical to the one above, passed immediately).
*
* @see https://github.com/BrianHenryIE/strauss/issues/47
*/
public function testDoesNotDoublePrefixAlreadyUpdatedNamespace(): void
{

$contents = 'namespace Dargon\\Dependencies\\Dragon\\Form;';

$namespace = "Dragon";
$prefix = "Dargon\\Dependencies\\";
$replacement = $prefix . $namespace;

$config = $this->createMock(StraussConfig::class);

$replacer = new Prefixer($config, __DIR__);
$result = $replacer->replaceNamespace($contents, $namespace, $replacement);

$this->assertNotEquals('namespace Dargon\\Dependencies\\Dargon\\Dependencies\\Dragon\\Form;', $result);
$this->assertEquals('namespace Dargon\\Dependencies\\Dragon\\Form;', $result);
}

/**
* @author markjaquith
*/
Expand Down

0 comments on commit 08ba84f

Please sign in to comment.