From 5196baf185e8283ea1909a75d5e4b7ab7f2729d4 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 14 Mar 2017 09:25:45 +0100 Subject: [PATCH 1/5] FEATURE: Migrate legacy class names after "new" Relates: #4 --- Readme.rst | 1 + .../InstantiationWithNewSniff.php | 59 +++++++++++++++++++ 2 files changed, 60 insertions(+) create mode 100644 src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithNewSniff.php diff --git a/Readme.rst b/Readme.rst index 228c3b6..e5f0876 100644 --- a/Readme.rst +++ b/Readme.rst @@ -51,6 +51,7 @@ new ones like ``\TYPO3\Extbase\...``. This is done for: - Typehints in methods and function like injects. +- Instantiation through ``new``. What does it look like? ======================= diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithNewSniff.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithNewSniff.php new file mode 100644 index 0000000..d98dc79 --- /dev/null +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithNewSniff.php @@ -0,0 +1,59 @@ + + * + * 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. + */ + +/** + * Detect and migrate static calls to old legacy classnames. + */ +class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithNewSniff implements PHP_CodeSniffer_Sniff +{ + use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait; + + /** + * Returns the token types that this sniff is interested in. + * + * @return array + */ + public function register() + { + return [T_NEW]; + } + + /** + * Processes the tokens that this sniff is interested in. + * + * @param PHP_CodeSniffer_File $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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $classnamePosition = $phpcsFile->findNext(T_STRING, $stackPtr); + if ($classnamePosition === false) { + return; + } + $classname = $tokens[$classnamePosition]['content']; + + $this->addFixableError($phpcsFile, $classnamePosition, $classname); + } +} From 4134ba98f34fa49811c47413623d26e9f4f3f686 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 14 Mar 2017 10:06:07 +0100 Subject: [PATCH 2/5] FEATURE: Migrate makeInstance instantiations Relates: #4 --- Readme.rst | 2 + .../ClassnameCheckerTrait.php | 40 ++++++++ .../InstantiationWithMakeInstanceSniff.php | 97 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php diff --git a/Readme.rst b/Readme.rst index e5f0876..8cda268 100644 --- a/Readme.rst +++ b/Readme.rst @@ -53,6 +53,8 @@ new ones like ``\TYPO3\Extbase\...``. This is done for: - Instantiation through ``new``. +- Instantiation through ``makeInstance``. + What does it look like? ======================= diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php index 86ca738..e709022 100644 --- a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php @@ -21,6 +21,7 @@ namespace Typo3Update\Sniffs\LegacyClassnames; */ use PHP_CodeSniffer_File as PhpcsFile; +use PHP_CodeSniffer_Tokens as Tokens; /** * Provide common uses for all sniffs. @@ -108,4 +109,43 @@ trait ClassnameCheckerTrait { return $classname; } + + /** + * Check whether current stackPtr is a function call. + * + * Code taken from PEAR_Sniffs_Functions_FunctionCallSignatureSniff for reuse. + * + * @param PhpCsFile $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + protected function isFunctionCall(PhpCsFile $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + // Find the next non-empty token. + $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { + // Not a function call. + return false; + } + + if (isset($tokens[$openBracket]['parenthesis_closer']) === false) { + // Not a function call. + return false; + } + + // Find the previous non-empty token. + $search = Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true); + if ($tokens[$previous]['code'] === T_FUNCTION) { + // It's a function definition, not a function call. + return false; + } + + return true; + } } diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php new file mode 100644 index 0000000..0638b50 --- /dev/null +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php @@ -0,0 +1,97 @@ + + * + * 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_Tokens as Tokens; + +/** + * Detect and migrate static calls to old legacy classnames. + */ +class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithMakeInstanceSniff implements PHP_CodeSniffer_Sniff +{ + use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait; + + /** + * Original token content for reuse accross methods. + * @var string + */ + protected $originalTokenContent = ''; + + /** + * Returns the token types that this sniff is interested in. + * + * @return array + */ + public function register() + { + return Tokens::$functionNameTokens; + } + + /** + * Processes the tokens that this sniff is interested in. + * + * @param PHP_CodeSniffer_File $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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + if (!$this->isFunctionCall($phpcsFile, $stackPtr)) { + return; + } + $tokens = $phpcsFile->getTokens(); + + if ($tokens[$stackPtr]['content'] !== 'makeInstance') { + return; + } + + $classnamePosition = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr); + if ($classnamePosition === false) { + return; + } + + $classname = trim($tokens[$classnamePosition]['content'], '\'"'); + $this->originalTokenContent = $tokens[$classnamePosition]['content']; + $this->addFixableError($phpcsFile, $classnamePosition, $classname); + } + + /** + * As token contains more then just class name, we have to build new content ourself. + * + * @param string $classname + * @return string + */ + protected function getTokenForReplacement($classname) + { + $stringSign = $this->originalTokenContent[0]; + $token = explode($stringSign, $this->originalTokenContent); + $token[1] = $classname; + + // Migrate double quote to single quote. + // This way no escaping of backslashes in class names is necessary. + if ($stringSign === '"') { + $stringSign = "'"; + } + + return implode($stringSign, $token); + } +} From 20722f26d19505d2d4e00b7ddfb136d7f7d09d16 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 14 Mar 2017 11:37:06 +0100 Subject: [PATCH 3/5] TASK: Refactor code base * As method does not belong to class renaming, but is a general method, it was moved to a new trait. Relates: #4 --- .../Sniffs/ExtendedPhpCsSupportTrait.php | 69 +++++++++++++++++++ .../ClassnameCheckerTrait.php | 41 +---------- .../InstantiationWithMakeInstanceSniff.php | 1 + 3 files changed, 71 insertions(+), 40 deletions(-) create mode 100644 src/Standards/Typo3Update/Sniffs/ExtendedPhpCsSupportTrait.php diff --git a/src/Standards/Typo3Update/Sniffs/ExtendedPhpCsSupportTrait.php b/src/Standards/Typo3Update/Sniffs/ExtendedPhpCsSupportTrait.php new file mode 100644 index 0000000..2368d4d --- /dev/null +++ b/src/Standards/Typo3Update/Sniffs/ExtendedPhpCsSupportTrait.php @@ -0,0 +1,69 @@ + + * + * 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 PHP_CodeSniffer_Tokens as Tokens; + +/** + * Provide common uses for all sniffs. + */ +trait ExtendedPhpCsSupportTrait +{ + /** + * Check whether current stackPtr is a function call. + * + * Code taken from PEAR_Sniffs_Functions_FunctionCallSignatureSniff for reuse. + * + * @param PhpCsFile $phpcsFile + * @param int $stackPtr + * + * @return bool + */ + protected function isFunctionCall(PhpCsFile $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + + // Find the next non-empty token. + $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); + + if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { + // Not a function call. + return false; + } + + if (isset($tokens[$openBracket]['parenthesis_closer']) === false) { + // Not a function call. + return false; + } + + // Find the previous non-empty token. + $search = Tokens::$emptyTokens; + $search[] = T_BITWISE_AND; + $previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true); + if ($tokens[$previous]['code'] === T_FUNCTION) { + // It's a function definition, not a function call. + return false; + } + + return true; + } +} diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php index e709022..5537b0f 100644 --- a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php @@ -24,7 +24,7 @@ use PHP_CodeSniffer_File as PhpcsFile; use PHP_CodeSniffer_Tokens as Tokens; /** - * Provide common uses for all sniffs. + * Provide common uses for all sniffs, regarding class name checks. */ trait ClassnameCheckerTrait { @@ -109,43 +109,4 @@ trait ClassnameCheckerTrait { return $classname; } - - /** - * Check whether current stackPtr is a function call. - * - * Code taken from PEAR_Sniffs_Functions_FunctionCallSignatureSniff for reuse. - * - * @param PhpCsFile $phpcsFile - * @param int $stackPtr - * - * @return bool - */ - protected function isFunctionCall(PhpCsFile $phpcsFile, $stackPtr) - { - $tokens = $phpcsFile->getTokens(); - - // Find the next non-empty token. - $openBracket = $phpcsFile->findNext(Tokens::$emptyTokens, ($stackPtr + 1), null, true); - - if ($tokens[$openBracket]['code'] !== T_OPEN_PARENTHESIS) { - // Not a function call. - return false; - } - - if (isset($tokens[$openBracket]['parenthesis_closer']) === false) { - // Not a function call. - return false; - } - - // Find the previous non-empty token. - $search = Tokens::$emptyTokens; - $search[] = T_BITWISE_AND; - $previous = $phpcsFile->findPrevious($search, ($stackPtr - 1), null, true); - if ($tokens[$previous]['code'] === T_FUNCTION) { - // It's a function definition, not a function call. - return false; - } - - return true; - } } diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php index 0638b50..94bb175 100644 --- a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithMakeInstanceSniff.php @@ -27,6 +27,7 @@ use PHP_CodeSniffer_Tokens as Tokens; class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithMakeInstanceSniff implements PHP_CodeSniffer_Sniff { use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait; + use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; /** * Original token content for reuse accross methods. From 187dc918a98e551e86ecc8873be394f5af0fa542 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 16 Mar 2017 14:12:58 +0100 Subject: [PATCH 4/5] FEATURE: Migrate instantiation through object manager * Mark "create" as deprecated with a warning. * Migrate first argument of "create" and "get" calls if they are deprecated. Resolves: #4 --- Readme.rst | 9 ++ .../InstantiationWithObjectManagerSniff.php | 102 ++++++++++++++++++ 2 files changed, 111 insertions(+) create mode 100644 src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithObjectManagerSniff.php diff --git a/Readme.rst b/Readme.rst index 1991336..7d6a6cf 100644 --- a/Readme.rst +++ b/Readme.rst @@ -60,6 +60,15 @@ new ones like ``\TYPO3\Extbase\...``. This is done for: - Instantiation through ``makeInstance``. +- Instantiation through ``ObjectManager``, check afterwards as this is static and all function calls + using ``get`` and ``create`` will be adjusted. Might be useful to exclude this sniff and run it + separately. + +Also we check for the following deprecated calls: + +- Check for ``create`` on ``ObjectManager``, which is "stupid" just all ``create`` calls are marked + with a warning. + What does it look like? ======================= diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithObjectManagerSniff.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithObjectManagerSniff.php new file mode 100644 index 0000000..c16b47d --- /dev/null +++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/InstantiationWithObjectManagerSniff.php @@ -0,0 +1,102 @@ + + * + * 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_Tokens as Tokens; + +/** + * Detect and migrate static calls to old legacy classnames. + */ +class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithObjectManagerSniff implements PHP_CodeSniffer_Sniff +{ + use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait; + use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; + + /** + * Returns the token types that this sniff is interested in. + * + * @return array + */ + public function register() + { + return Tokens::$functionNameTokens; + } + + /** + * Processes the tokens that this sniff is interested in. + * + * @param PHP_CodeSniffer_File $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(PHP_CodeSniffer_File $phpcsFile, $stackPtr) + { + if (!$this->isFunctionCall($phpcsFile, $stackPtr)) { + return; + } + $tokens = $phpcsFile->getTokens(); + + $functionName = $tokens[$stackPtr]['content']; + if (!in_array($functionName, ['get', 'create'])) { + return; + } + + $classnamePosition = $phpcsFile->findNext(T_CONSTANT_ENCAPSED_STRING, $stackPtr); + if ($classnamePosition === false) { + return; + } + + if ($functionName === 'create') { + $phpcsFile->addWarning( + 'The "create" method of ObjectManager is no longer supported, please migrate to "get".', + $stackPtr, + 'mightBeDeprecatedMethod', + ['create'] + ); + } + + $classname = trim($tokens[$classnamePosition]['content'], '\'"'); + $this->originalTokenContent = $tokens[$classnamePosition]['content']; + $this->addFixableError($phpcsFile, $classnamePosition, $classname); + } + + /** + * As token contains more then just class name, we have to build new content ourself. + * + * @param string $classname + * @return string + */ + protected function getTokenForReplacement($classname) + { + $stringSign = $this->originalTokenContent[0]; + $token = explode($stringSign, $this->originalTokenContent); + $token[1] = $classname; + + // Migrate double quote to single quote. + // This way no escaping of backslashes in class names is necessary. + if ($stringSign === '"') { + $stringSign = "'"; + } + + return implode($stringSign, $token); + } +} From bbc1daf10565a6a6bcb4b9429d9e96adf2a0ee2f Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Thu, 16 Mar 2017 14:17:40 +0100 Subject: [PATCH 5/5] TASK: Add further information to doc, what exactly is supported Relates: #4 --- Readme.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Readme.rst b/Readme.rst index 7d6a6cf..4aba35d 100644 --- a/Readme.rst +++ b/Readme.rst @@ -58,11 +58,12 @@ new ones like ``\TYPO3\Extbase\...``. This is done for: - Instantiation through ``new``. -- Instantiation through ``makeInstance``. +- Instantiation through ``makeInstance``. Only Classnames in Strings are supported, no ``::class``. - Instantiation through ``ObjectManager``, check afterwards as this is static and all function calls using ``get`` and ``create`` will be adjusted. Might be useful to exclude this sniff and run it separately. + Only Classnames in Strings are supported, no ``::class``. Also we check for the following deprecated calls: