From fcb778d903fa0aa95e15d5fd1d9d252799387c6a Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 21 Mar 2017 16:35:25 +0100 Subject: [PATCH] WIP|FEATURE: Provide sniff to convert old legacy class definitions * Insert missing namespace, based on existing class name. * Allow configuration of Vendor to use. * Will not adjust uses of the class. * All other sniffs are broken right now, they need to be adjusted to new abstract class. Relates: #36 --- Readme.rst | 18 ++ ...Trait.php => AbstractClassnameChecker.php} | 26 ++- .../MissingNamespaceSniff.php | 157 ++++++++++++++++++ src/Standards/Typo3Update/ruleset.xml | 4 + 4 files changed, 200 insertions(+), 5 deletions(-) rename src/Standards/Typo3Update/Sniffs/LegacyClassnames/{ClassnameCheckerTrait.php => AbstractClassnameChecker.php} (90%) create mode 100644 src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php diff --git a/Readme.rst b/Readme.rst index 86f6a6e..1c13268 100644 --- a/Readme.rst +++ b/Readme.rst @@ -69,6 +69,8 @@ new ones like ``\TYPO3\Extbase\...``. This is done for: - ``catch`` of legacy class names. +- Convert old legacy class definitions in extensions to namespace ones. + Also we check for the following deprecated calls: - Check for ``create`` on ``ObjectManager``, which is "stupid" just all ``create`` calls are marked @@ -136,3 +138,19 @@ Typo3Update.LegacyClassnames.DocComment: ``allowedTags`` + +``vendor`` + Configure your vendor through ``ruleset.xml`` or using ``--runtime-set``. Default is + ``YourCompany``. + + Example: + +.. code:: xml + + + +Example: + +.. code:: bash + + --runtime-set vendor YourVendor diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php similarity index 90% rename from src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php rename to src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php index d6a2fe2..305213e 100644 --- a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php @@ -20,12 +20,14 @@ namespace Typo3Update\Sniffs\LegacyClassnames; * 02110-1301, USA. */ +use PHP_CodeSniffer as PhpCs; use PHP_CodeSniffer_File as PhpcsFile; +use PHP_CodeSniffer_Sniff as PhpCsSniff; /** * Provide common uses for all sniffs, regarding class name checks. */ -trait ClassnameCheckerTrait +abstract class AbstractClassnameChecker implements PhpCsSniff { /** * Contains mapping from old -> new class names. @@ -43,6 +45,20 @@ trait ClassnameCheckerTrait */ public $legacyExtensions = ['Extbase', 'Fluid']; + /** + * Returns the configured vendor, e.g. to generate new namespaces. + * + * @return string + */ + protected function getVendor() + { + $vendor = PhpCs::getConfigData('vendor'); + if (!$vendor) { + $vendor = 'YourCompany'; + } + return trim($vendor, '\\/'); + } + /** * @param string $mappingFile File containing php array for mapping. */ @@ -103,7 +119,7 @@ trait ClassnameCheckerTrait * @param string $classname * @return bool */ - private function isLegacyClassname($classname) + protected function isLegacyClassname($classname) { $this->initialize(); return isset($this->legacyClassnames[strtolower($classname)]); @@ -138,7 +154,7 @@ trait ClassnameCheckerTrait * @param string $classname * @return string */ - private function getNewClassname($classname) + protected function getNewClassname($classname) { $this->initialize(); return $this->legacyClassnames[strtolower($classname)]; @@ -200,10 +216,10 @@ trait ClassnameCheckerTrait * @param int $classnamePosition * @param string $classname */ - private function replaceLegacyClassname(PhpcsFile $phpcsFile, $classnamePosition, $classname) + protected function replaceLegacyClassname(PhpcsFile $phpcsFile, $classnamePosition, $classname, $forceEmptyPrefix = false) { $prefix = '\\'; - if ($phpcsFile->getTokens()[$classnamePosition -1]['code'] === T_NS_SEPARATOR) { + if ($forceEmptyPrefix || $phpcsFile->getTokens()[$classnamePosition -1]['code'] === T_NS_SEPARATOR) { $prefix = ''; } diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php new file mode 100644 index 0000000..2d74d70 --- /dev/null +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php @@ -0,0 +1,157 @@ + + * + * 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 PHP_CodeSniffer_File as PhpCsFile; +use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker; + +/** + * Detect missing namespaces for class definitions. + */ +class Typo3Update_Sniffs_LegacyClassnames_MissingNamespaceSniff extends AbstractClassnameChecker +{ + /** + * Returns the token types that this sniff is interested in. + * + * @return array + */ + public function register() + { + return [T_CLASS]; + } + + /** + * Processes the tokens that this sniff is interested in. + * + * @param PhpCsFile $phpcsFile The file where the token was found. + * @param int $stackPtr The position in the stack where + * the token was found. + * + * @return void + */ + public function process(PhpCsFile $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + $namespacePosition = $phpcsFile->findPrevious(T_NAMESPACE, $stackPtr); + if ($namespacePosition !== false) { + return; + } + $classnamePosition = $phpcsFile->findNext(T_STRING, $stackPtr); + if ($classnamePosition === false) { + return; + } + $classname = $tokens[$classnamePosition]['content']; + + $this->addFixableError($phpcsFile, $classnamePosition, $classname); + } + + /** + * Overwrite as we don't look up the classname, but check whether the style is legacy. + * + * @param string $classname + * @return bool + */ + protected function isLegacyClassname($classname) + { + return strpos($classname, 'Tx_') === 0; + } + + /** + * @param string $classname + * @return string + */ + protected function getNewClassname($classname) + { + return substr($classname, strrpos($classname, '_') + 1); + } + + /** + * + * @param PhpCsFile $phpcsFile + * @param int $classnamePosition + * @param string $classname + */ + protected function replaceLegacyClassname(PhpCsFile $phpcsFile, $classnamePosition, $classname, $forceEmptyPrefix = true) + { + parent::replaceLegacyClassname($phpcsFile, $classnamePosition, $classname, $forceEmptyPrefix); + + $tokens = $phpcsFile->getTokens(); + $lineEndings = PhpCsFile::detectLineEndings($phpcsFile->getFilename()); + $suffix = $lineEndings; + + // TODO, does not work. + if ($tokens[1] === "\n") { + $suffix .= $lineEndings; + } + + $phpcsFile->fixer->replaceToken( + $this->getNamespacePosition($phpcsFile), + 'getNamespaceDefinition($classname) . $suffix + ); + } + + /** + * @param PhpCsFile $phpcsFile + * @return int + */ + protected function getNamespacePosition(PhpCsFile $phpcsFile) + { + return $phpcsFile->findNext(T_OPEN_TAG, 0); + } + + /** + * Returns whole statement to define namespace. + * + * E.g. namespace VENDOR\ExtName\FolderName; + * + * @param string $classname + * @return string + */ + protected function getNamespaceDefinition($classname) + { + $vendor = trim($this->getVendor(), '\\/'); + + return 'namespace ' + . $vendor + . '\\' + . $this->getNamespace($classname) + . ';' + ; + } + + /** + * Returns namespace, without vendor, based on legacy class name. + * + * E.g. Tx_ExtName_FolderName_FileName -> ExtName\FolderName + * + * @param string $classname + * @return string + */ + protected function getNamespace($classname) + { + $classnameParts = explode('_', $classname); + + unset($classnameParts[0]); // Remove Tx_ + unset($classnameParts[count($classnameParts)]); // Remove class name itself. + + return implode('\\', $classnameParts); + } +} diff --git a/src/Standards/Typo3Update/ruleset.xml b/src/Standards/Typo3Update/ruleset.xml index c8e6a69..260cadb 100644 --- a/src/Standards/Typo3Update/ruleset.xml +++ b/src/Standards/Typo3Update/ruleset.xml @@ -12,4 +12,8 @@ + + + Legacy class definitions are not allowed; found "%s". Wrap your class inside a namespace. +