diff --git a/composer.json b/composer.json index 623bfde..350d94c 100644 --- a/composer.json +++ b/composer.json @@ -31,6 +31,7 @@ }, "require-dev": { "phpunit/phpunit": "5.7.*", + "mikey179/vfsStream": "1.6.*", "symfony/finder": "3.2.*", "phpmd/phpmd": "2.6.*", "pdepend/pdepend": "2.5.*" diff --git a/src/Standards/Typo3Update/Feature/LegacyClassnameMapping.php b/src/Standards/Typo3Update/Feature/LegacyClassnameMapping.php index dae9989..a60c92b 100644 --- a/src/Standards/Typo3Update/Feature/LegacyClassnameMapping.php +++ b/src/Standards/Typo3Update/Feature/LegacyClassnameMapping.php @@ -69,15 +69,32 @@ final class LegacyClassnameMapping // Singleton implementation - End /** - * Contains mappings as defined by composer for alias mapping. * @var array */ protected $typo3Mappings = []; + /** + * @var array + */ protected $typo3MappingsKeys = []; + /** + * @var array + */ protected $mappings = []; + /** + * @var array + */ protected $mappingsKeys = []; + /** + * @var bool + */ + protected $dirty = false; + + /** + * @param array $originalArray + * @param array $targetVariable + */ protected function buildKeyArray($originalArray, &$targetVariable) { foreach (array_keys($originalArray) as $key) { @@ -109,7 +126,6 @@ final class LegacyClassnameMapping */ public function getNewClassname($classname) { - $lowerVersion = strtolower($classname); if ($this->isLegacyTypo3Classname($classname) || $this->isLegacyTypo3Classname(strtolower($classname))) { return $this->typo3Mappings[$this->getTypo3MappingKey($classname)]; } @@ -117,6 +133,10 @@ final class LegacyClassnameMapping return $this>mappings[$this->getLegacyMappingKey($classname)]; } + /** + * @param string $classname + * @return string + */ protected function getTypo3MappingKey($classname) { $lowerVersion = strtolower($classname); @@ -127,6 +147,10 @@ final class LegacyClassnameMapping return $classname; } + /** + * @param string $classname + * @return string + */ protected function getLegacyMappingKey($classname) { $lowerVersion = strtolower($classname); @@ -137,11 +161,19 @@ final class LegacyClassnameMapping return $classname; } + /** + * @param string $classname + * @return bool + */ protected function isLegacyTypo3Classname($classname) { return isset($this->typo3MappingsKeys[$classname]); } + /** + * @param string $classname + * @return bool + */ protected function isLegacyMappingClassname($classname) { return isset($this->mappingsKeys[$classname]); @@ -159,6 +191,21 @@ final class LegacyClassnameMapping public function addLegacyClassname($legacyClassname, $newClassname) { $this->mappings[$legacyClassname] = $newClassname; + $this->mappingsKeys[strtolower($legacyClassname)] = $legacyClassname; + $this->dirty = true; + } + + public function persistMappings() + { + if ($this->dirty === false) { + return; + } + + file_put_contents( + Options::getMappingFile(), + 'mappings, true) . ';' . PHP_EOL + ); + $this->dirty = false; } /** @@ -166,16 +213,6 @@ final class LegacyClassnameMapping */ public function __destruct() { - // For some reasons desctruct is called multiple times, while construct - // is called once. Until we know the issue and fix it, this is our - // workaround to not break the file and do stuff in an unkown instance. - if ($this !== static::$instance) { - return; - } - - file_put_contents( - Options::getMappingFile(), - 'mappings, true) . ';' - ); + $this->persistMappings(); } } diff --git a/tests/Feature/LegacyClassnameMappingTest.php b/tests/Feature/LegacyClassnameMappingTest.php new file mode 100644 index 0000000..f420e7f --- /dev/null +++ b/tests/Feature/LegacyClassnameMappingTest.php @@ -0,0 +1,110 @@ + + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +use PHPUnit\Framework\TestCase; +use Typo3Update\Feature\LegacyClassnameMapping; +use org\bovigo\vfs\vfsStream; + +class LegacyClassnameMappingTest extends TestCase +{ + /** + * @var \org\bovigo\vfs\vfsStreamDirectory + */ + protected $fileSystem; + + /** + * @var LegacyClassnameMapping + */ + protected $subject; + + /** + * @param string $fileName + * @return string + */ + protected function getFixturePath($fileName) + { + return implode(DIRECTORY_SEPARATOR, [ + __DIR__, '..', 'Fixtures', 'Standards','Typo3Update', + 'Feature', 'LegacyClassnameMapping', $fileName, + ]); + } + + public function setUp() + { + $this->fileSystem = vfsStream::setup('root', null, [ + 'LegacyClassnames.php' => file_get_contents($this->getFixturePath('MappingContent.php')), + ]); + + $GLOBALS['PHP_CODESNIFFER_CONFIG_DATA'] = [ + 'mappingFile' => vfsStream::url('root/LegacyClassnames.php'), + ]; + + $this->subject = LegacyClassnameMapping::getInstance(); + } + + /** + * @test + */ + public function insensitivityLookupWorks() + { + $this->assertFalse( + $this->subject->isLegacyClassname('Tx_About_Controller_Aboutcontroller', false), + 'Classname was returned to be legacy but should not due to lowercase version and case sensitivity.' + ); + $this->assertTrue( + $this->subject->isLegacyClassname('Tx_About_Controller_Aboutcontroller'), + 'Classname was not returned to be legacy but should due to case insensitivity.' + ); + } + + /** + * @test + */ + public function addingLegacyClassnamesWillAdjustLookupAndBePersisted() + { + $this->assertFalse( + $this->subject->isLegacyClassname('Tx_ExtName_Controller_ExampleController'), + 'Classname is not configured but returned to be legacy.' + ); + + $this->subject->addLegacyClassname('Tx_ExtName_Controller_ExampleController', '\\Vendor\\ExtName\\Controller\\ExampleController'); + + $this->assertTrue( + $this->subject->isLegacyClassname('Tx_ExtName_Controller_ExampleController'), + 'Classname is configured but not returned to be legacy.' + ); + + $this->subject->persistMappings(); + + $this->assertSame( + file_get_contents($this->getFixturePath('ExpectedMappingContent.php')), + file_get_contents(vfsStream::url('root/LegacyClassnames.php')), + 'Persisted mappings are not as expected.' + ); + } + + public function tearDown() + { + unset($this->subject); + unset($this->fileSystem); + } +} diff --git a/tests/Fixtures/Standards/Typo3Update/Feature/LegacyClassnameMapping/ExpectedMappingContent.php b/tests/Fixtures/Standards/Typo3Update/Feature/LegacyClassnameMapping/ExpectedMappingContent.php new file mode 100644 index 0000000..7450d61 --- /dev/null +++ b/tests/Fixtures/Standards/Typo3Update/Feature/LegacyClassnameMapping/ExpectedMappingContent.php @@ -0,0 +1,4 @@ + '\\Vendor\\ExtName\\Controller\\ExampleController', +); diff --git a/tests/Fixtures/Standards/Typo3Update/Feature/LegacyClassnameMapping/MappingContent.php b/tests/Fixtures/Standards/Typo3Update/Feature/LegacyClassnameMapping/MappingContent.php new file mode 100644 index 0000000..aa85dce --- /dev/null +++ b/tests/Fixtures/Standards/Typo3Update/Feature/LegacyClassnameMapping/MappingContent.php @@ -0,0 +1,3 @@ +