diff --git a/Readme.rst b/Readme.rst index 8d8b781..cf19a83 100644 --- a/Readme.rst +++ b/Readme.rst @@ -71,6 +71,13 @@ new ones like ``\TYPO3\Extbase\...``. This is done for: - Convert old legacy class definitions in extensions to namespace ones. +- Convert usage of previously converted class definitions. On first run the definition will be + converted, on second the usage. This is due to the fact, that PHPCS might find the definition + after the usage, so please run twice. + + *NOTE* The configured file will be updated after each run, for each converted class, trait and + interface definition. + - Add missing vendor to plugin and module registrations and configurations. You might want to set this to non fixable and warning if you already provide the vendor inside a single Variable, together with your extension key, as this is not recognized. So the following diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php index 6019cdc..b72e1d2 100644 --- a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php @@ -22,6 +22,7 @@ namespace Typo3Update\Sniffs\LegacyClassnames; use PHP_CodeSniffer_File as PhpCsFile; use PHP_CodeSniffer_Sniff as PhpCsSniff; +use Typo3Update\Sniffs\LegacyClassnames\Mapping; use Typo3Update\Sniffs\OptionsAccessTrait; /** @@ -31,12 +32,6 @@ abstract class AbstractClassnameChecker implements PhpCsSniff { use OptionsAccessTrait; - /** - * Contains mapping from old -> new class names. - * @var array - */ - private $legacyClassnames = []; - /** * A list of extension names that might contain legacy class names. * Used to check clas names for warnings. @@ -47,17 +42,9 @@ abstract class AbstractClassnameChecker implements PhpCsSniff */ public $legacyExtensions = ['Extbase', 'Fluid']; - /** - * @param string $mappingFile File containing php array for mapping. - */ - private function initialize($mappingFile = __DIR__ . '/../../../../../LegacyClassnames.php') + public function __construct() { - if ($this->legacyClassnames !== []) { - return; - } - - $legacyClassnames = require $mappingFile; - $this->legacyClassnames = $legacyClassnames['aliasToClassNameMapping']; + $this->legacyMapping = Mapping::getInstance(); } /** @@ -111,8 +98,7 @@ abstract class AbstractClassnameChecker implements PhpCsSniff */ protected function isLegacyClassname($classname) { - $this->initialize(); - return isset($this->legacyClassnames[strtolower($classname)]); + return $this->legacyMapping->isLegacyClassname($classname); } /** @@ -146,8 +132,21 @@ abstract class AbstractClassnameChecker implements PhpCsSniff */ protected function getNewClassname($classname) { - $this->initialize(); - return $this->legacyClassnames[strtolower($classname)]; + return $this->legacyMapping->getNewClassname($classname); + } + + /** + * Use to add new mappings found during parsing. + * E.g. in MissingNamespaceSniff old class definitions are fixed and a new mapping exists afterwards. + * + * @param string $legacyClassname + * @param string $newClassname + * + * @return void + */ + protected function addLegacyClassname($legacyClassname, $newClassname) + { + $this->legacyMapping->addLegacyClassname($legacyClassname, $newClassname); } /** diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/Mapping.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/Mapping.php new file mode 100644 index 0000000..885ecfd --- /dev/null +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/Mapping.php @@ -0,0 +1,124 @@ + + * + * 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 Typo3Update\Sniffs\OptionsAccessTrait; + +/** + * Singleton wrapper for mappings. + * + * Will check the configured file for whether a class is legacy and provides further methods. + * Also can update to add new migrated class names. + */ +class Mapping +{ + use OptionsAccessTrait; + + // Singleton implementation - Start + static protected $instance = null; + public static function getInstance() + { + if (static::$instance === null) { + static::$instance = new Mapping(); + } + + return static::$instance; + } + private function __clone() + { + } + private function __wakeup() + { + } + private function __construct() + { + // $mappingFile = $this->getMappingFile(); + $mappingFile = __DIR__ . '/../../../../../LegacyClassnames.php'; + + $this->mappings = require $mappingFile; + } + // Singleton implementation - End + + /** + * Contains mappings as defined by composer for alias mapping. + * @var array + */ + protected $mappings = []; + + /** + * Checks whether a mapping exists for the given $classname, + * indicating it's legacy. + * + * @param string $classname + * @return bool + */ + public function isLegacyClassname($classname) + { + return isset($this->mappings['aliasToClassNameMapping'][strtolower($classname)]); + } + + /** + * @param string $classname + * @return string + */ + public function getNewClassname($classname) + { + return $this->mappings['aliasToClassNameMapping'][strtolower($classname)]; + } + + /** + * Use to add new mappings found during parsing. + * E.g. in MissingNamespaceSniff old class definitions are fixed and a new mapping exists afterwards. + * + * @param string $legacyClassname + * @param string $newClassname + * + * @return void + */ + public function addLegacyClassname($legacyClassname, $newClassname) + { + $key = strtolower($legacyClassname); + + $this->mappings['aliasToClassNameMapping'][$key] = $newClassname; + $this->mappings['classNameToAliasMapping'][$newClassname] = [$key => $key]; + } + + /** + * Used to persist new mappings. + */ + 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; + } + + // $mappingFile = $this->getMappingFile(); + $mappingFile = __DIR__ . '/../../../../../LegacyClassnames.php'; + + file_put_contents( + $mappingFile, + 'mappings, true) . ';' + ); + } +} diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php index 391c357..5374dd3 100644 --- a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php @@ -109,6 +109,10 @@ class Typo3Update_Sniffs_LegacyClassnames_MissingNamespaceSniff extends Abstract $this->getNamespacePosition($phpcsFile), 'getNamespaceDefinition($classname) . $suffix ); + $this->addLegacyClassname( + $classname, + $this->getNamespace($classname) . '\\' . $this->getNewClassname($classname) + ); } /** @@ -130,14 +134,7 @@ class Typo3Update_Sniffs_LegacyClassnames_MissingNamespaceSniff extends Abstract */ protected function getNamespaceDefinition($classname) { - $vendor = trim($this->getVendor(), '\\/'); - - return 'namespace ' - . $vendor - . '\\' - . $this->getNamespace($classname) - . ';' - ; + return 'namespace ' . $this->getNamespace($classname) . ';'; } /** @@ -150,11 +147,12 @@ class Typo3Update_Sniffs_LegacyClassnames_MissingNamespaceSniff extends Abstract */ protected function getNamespace($classname) { + $vendor = trim($this->getVendor(), '\\/'); $classnameParts = explode('_', $classname); unset($classnameParts[0]); // Remove Tx_ unset($classnameParts[count($classnameParts)]); // Remove class name itself. - return implode('\\', $classnameParts); + return $vendor . '\\' . implode('\\', $classnameParts); } }