From 1a081900b23410f92dd27fe56418d6151e0b2419 Mon Sep 17 00:00:00 2001 From: Daniel Siepmann Date: Tue, 4 Apr 2017 09:59:28 +0200 Subject: [PATCH] WIP|FEATURE: Add removed constants * Add configuration. * Refactor common base for functions and constants. * Add constants handling. Relates: #42 --- .../Configuration/Removed/Constants/7.0.yaml | 11 + .../Configuration/Removed/Constants/7.4.yaml | 8 + .../Configuration/Removed/Constants/7.5.yaml | 5 + .../Configuration/Removed/Constants/7.6.yaml | 14 + .../Sniffs/Removed/AbstractGenericUsage.php | 295 ++++++++++++++++++ .../Removed/GenericConstantUsageSniff.php | 268 ++-------------- .../Removed/GenericFunctionCallSniff.php | 234 +------------- 7 files changed, 377 insertions(+), 458 deletions(-) create mode 100644 src/Standards/Typo3Update/Configuration/Removed/Constants/7.0.yaml create mode 100644 src/Standards/Typo3Update/Configuration/Removed/Constants/7.4.yaml create mode 100644 src/Standards/Typo3Update/Configuration/Removed/Constants/7.5.yaml create mode 100644 src/Standards/Typo3Update/Configuration/Removed/Constants/7.6.yaml create mode 100644 src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php diff --git a/src/Standards/Typo3Update/Configuration/Removed/Constants/7.0.yaml b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.0.yaml new file mode 100644 index 0000000..49c13d4 --- /dev/null +++ b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.0.yaml @@ -0,0 +1,11 @@ +# Breaking changes in 7.0: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Index.html#breaking-changes +'7.0': + PATH_tslib: + replacement: 'The folder and constant no longer exist' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61459-RemovalTslib.html + \TYPO3\CMS\Core\Resource\AbstractFile::FILETYPE_SOFTWARE: + replacement: 'Use \TYPO3\CMS\Core\Resource\AbstractFile::FILETYPE_APPLICATION instead' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61859-FileTypeSoftwareRemoved.html + REQUIRED_EXTENSIONS: + replacement: null + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62416-DeprecatedCodeRemovalInCoreSysext.html#packagemanager diff --git a/src/Standards/Typo3Update/Configuration/Removed/Constants/7.4.yaml b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.4.yaml new file mode 100644 index 0000000..71130bb --- /dev/null +++ b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.4.yaml @@ -0,0 +1,8 @@ +# Breaking changes in 7.4: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.4/Index.html#breaking-changes +'7.4': + TYPO3_MOD_PATH: + replacement: 'It is required to route modules through typo3/mod.php from now on in case the module relies on the definition of those constants' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.4/Breaking-67987-RemovedEntryScriptHandling.html + PATH_typo3_mod: + replacement: 'It is required to route modules through typo3/mod.php from now on in case the module relies on the definition of those constants' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.4/Breaking-67987-RemovedEntryScriptHandling.html diff --git a/src/Standards/Typo3Update/Configuration/Removed/Constants/7.5.yaml b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.5.yaml new file mode 100644 index 0000000..7c02d6d --- /dev/null +++ b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.5.yaml @@ -0,0 +1,5 @@ +# Breaking changes in 7.5: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.5/Index.html#breaking-changes +'7.5': + TYPO3_URL_ORG: + replacement: 'Use TYPO3_URL_GENERAL instead' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.5/Breaking-68814-RemoveOfBaseConstantTYPO3_URL_ORG.html diff --git a/src/Standards/Typo3Update/Configuration/Removed/Constants/7.6.yaml b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.6.yaml new file mode 100644 index 0000000..0691b70 --- /dev/null +++ b/src/Standards/Typo3Update/Configuration/Removed/Constants/7.6.yaml @@ -0,0 +1,14 @@ +# Breaking changes in 7.6: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.6/Index.html#breaking-changes +'7.6': + \TYPO3\CMS\IndexedSearch\Controller\SearchFormController::WILDCARD_LEFT: + replacement: 'Use \TYPO3\CMS\IndexedSearch\Utility\LikeWildcard::LEFT instead' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.6/Breaking-69227-StringsForLikeAreNotProperlyEscaped.html + \TYPO3\CMS\IndexedSearch\Controller\SearchFormController::WILDCARD_RIGHT: + replacement: 'Use \TYPO3\CMS\IndexedSearch\Utility\LikeWildcard::RIGHT instead' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.6/Breaking-69227-StringsForLikeAreNotProperlyEscaped.html + \TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository::WILDCARD_LEFT: + replacement: 'Use \TYPO3\CMS\IndexedSearch\Utility\LikeWildcard::LEFT instead' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.6/Breaking-69227-StringsForLikeAreNotProperlyEscaped.html + \TYPO3\CMS\IndexedSearch\Domain\Repository\IndexSearchRepository::WILDCARD_RIGHT: + replacement: 'Use \TYPO3\CMS\IndexedSearch\Utility\LikeWildcard::RIGHT instead' + docsUrl: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.6/Breaking-69227-StringsForLikeAreNotProperlyEscaped.html diff --git a/src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php b/src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php new file mode 100644 index 0000000..3875dcd --- /dev/null +++ b/src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php @@ -0,0 +1,295 @@ + + * + * 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_Sniff as PhpCsSniff; +use PHP_CodeSniffer_Tokens as Tokens; +use Symfony\Component\Yaml\Yaml; + +/** + * Contains common functionality for removed code like constants or functions. + * + * Removed parts are configured using YAML-Files, for examples see src/Standards/Typo3Update/Configuration/Removed/Constants/7.0.yaml + * Also check out the configuration options in Readme.rst. + */ +abstract class AbstractGenericUsage implements PhpCsSniff +{ + use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; + use \Typo3Update\Sniffs\OptionsAccessTrait; + + /** + * Configuration to define removed code. + * + * @var array + */ + protected $configured = []; + + /** + * Constant for the current sniff instance. + * @var array + */ + protected $removed = []; + + /** + * TODO: Multiple files allowed, using glob ... + * to allow splitting per ext (extbase, fluid, ...) and TYPO3 Version 7.1, 7.0, ... + */ + public function __construct() + { + if ($this->configured === []) { + foreach ($this->getRemovedConfigFiles() as $file) { + $this->configured = array_merge( + $this->configured, + $this->prepareStructure(Yaml::parse(file_get_contents((string) $file))) + ); + } + } + } + + /** + * Return file names containing removed configurations. + * + * @return array + */ + abstract protected function getRemovedConfigFiles(); + + /** + * Prepares structure from config for later usage. + * + * @param array $typo3Versions + * @return array + */ + protected function prepareStructure(array $typo3Versions) + { + $newStructure = []; + + foreach ($typo3Versions as $typo3Version => $removals) { + foreach ($removals as $removed => $config) { + // Split static methods and methods. + $split = preg_split('/::|->/', $removed); + + $newStructure[$removed] = $config; + + $newStructure[$removed]['static'] = strpos($removed, '::') !== false; + $newStructure[$removed]['fqcn'] = null; + $newStructure[$removed]['class'] = null; + $newStructure[$removed]['name'] = $split[0]; + $newStructure[$removed]['version_removed'] = $typo3Version; + + // If split contains two parts, it's a class with method + if (isset($split[1])) { + $newStructure[$removed]['fqcn'] = $split[0]; + $newStructure[$removed]['class'] = array_slice( + explode('\\', $newStructure[$removed]['fqcn']), + -1 + )[0]; + $newStructure[$removed]['name'] = $split[1]; + } + }; + } + + return $newStructure; + } + + /** + * Processes the tokens that this sniff is interested in. + * + * This is the default implementation, as most of the time next T_STRING is + * the class name. This way only the register method has to be registered + * in default cases. + * + * @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) + { + if (!$this->isRemoved($phpcsFile, $stackPtr)) { + return; + } + + $this->addWarning($phpcsFile, $stackPtr); + } + + /** + * Check whether the current token is removed. + * + * @param PhpCsFile $phpcsFile + * @param int $stackPtr + * @return bool + */ + protected function isRemoved(PhpCsFile $phpcsFile, $stackPtr) + { + $tokens = $phpcsFile->getTokens(); + $staticPosition = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true); + + $name = $tokens[$stackPtr]['content']; + $isStatic = false; + $class = false; + + if ($staticPosition !== false) { + $isStatic = $tokens[$staticPosition]['code'] === T_DOUBLE_COLON; + } + + if ($isStatic) { + $class = $phpcsFile->findPrevious(T_STRING, $staticPosition, null, false, null, true); + if ($class !== false) { + $class = $tokens[$class]['content']; + } + } + + $this->removed = $this->getMatchingRemoved($name, $class, $isStatic); + return $this->removed !== []; + } + + /** + * Returns all matching removed functions for given arguments. + * + * @param string $name + * @param string $className The last part of the class name, splitted by namespaces. + * @param bool $isStatic + * + * @return array + */ + protected function getMatchingRemoved($name, $className, $isStatic) + { + // We will not match any static method, without the class name, at least for now. + // Otherwise we could handle them the same way as instance methods. + if ($isStatic === true && $className === false) { + return []; + } + + return array_filter( + $this->configured, + function ($config) use ($name, $isStatic, $className) { + return $name === $config['name'] + && $isStatic === $config['static'] + && ( + $className === $config['class'] + || $className === false + ) + ; + } + ); + } + + /** + * Add warning for the given token position. + * + * @param PhpCsFile $phpcsFile + * @param int $tokenPosition + * + * @return void + */ + protected function addWarning(PhpCsFile $phpcsFile, $tokenPosition) + { + foreach ($this->removed as $constant) { + $phpcsFile->addWarning( + 'Legacy calls are not allowed; found %s. Removed in %s. %s. See: %s', + $tokenPosition, + $this->getIdentifier($constant), + [ + $this->getOldUsage($constant), + $this->getRemovedVersion($constant), + $this->getReplacement($constant), + $this->getDocsUrl($constant), + ] + ); + } + } + + /** + * Identifier for configuring this specific error / warning through PHPCS. + * + * @param array $config The converted structure for a single function. + * + * @return string + */ + protected function getIdentifier(array $config) + { + $name = $config['name']; + if ($config['class']) { + $name = $config['class'] . '.' . $name; + } + + return $name; + } + + /** + * The original constant call, to allow user to check matches. + * + * As we match the constant name, that can be provided by multiple classes, + * you should provide an example, so users can check that this is the + * legacy one. + * + * @param array $config The converted structure for a single constant. + * + * @return string + */ + abstract protected function getOldUsage(array $config); + + /** + * Returns TYPO3 version when the current constant was removed. + * + * To let user decide whether this is important for him. + * + * @param array $config The converted structure for a single constant. + * + * @return string + */ + protected function getRemovedVersion(array $config) + { + return $config['version_removed']; + } + + /** + * The new constant call, or information how to migrate. + * + * To provide feedback for user to ease migration. + * + * @param array $config The converted structure for a single constant. + * + * @return string + */ + protected function getReplacement(array $config) + { + $newCall = $config['replacement']; + if ($newCall !== null) { + return $newCall; + } + return 'There is no replacement, just remove call'; + } + + /** + * Allow user to lookup the official docs related to this deprecation / breaking change. + * + * @param array $config The converted structure for a single constant. + * + * @return string + */ + protected function getDocsUrl(array $config) + { + return $config['docsUrl']; + } +} diff --git a/src/Standards/Typo3Update/Sniffs/Removed/GenericConstantUsageSniff.php b/src/Standards/Typo3Update/Sniffs/Removed/GenericConstantUsageSniff.php index c9ed1c1..adb479d 100644 --- a/src/Standards/Typo3Update/Sniffs/Removed/GenericConstantUsageSniff.php +++ b/src/Standards/Typo3Update/Sniffs/Removed/GenericConstantUsageSniff.php @@ -20,85 +20,53 @@ */ use PHP_CodeSniffer_File as PhpCsFile; -use PHP_CodeSniffer_Sniff as PhpCsSniff; -use PHP_CodeSniffer_Tokens as Tokens; -use Symfony\Component\Yaml\Yaml; +use Typo3Update\Sniffs\Removed\AbstractGenericUsage; /** + * Sniff that handles all calls to removed constants. * + * Removed constants are configured using YAML-Files, for examples see src/Standards/Typo3Update/Configuration/Removed/Constants/7.0.yaml + * Also check out the configuration options in Readme.rst. */ -class Typo3Update_Sniffs_Removed_GenericConstantUsageCallSniff implements PhpCsSniff +class Typo3Update_Sniffs_Removed_GenericConstantUsageSniff extends AbstractGenericUsage { - use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; - use \Typo3Update\Sniffs\OptionsAccessTrait; - /** - * Configuration to define removed constants. + * Return file names containing removed configurations. * - * @var array + * @return array */ - protected static $removedConstants = []; - - /** - * Constant for the current sniff instance. - * @var array - */ - private $removedConstant = []; - - /** - * TODO: Multiple files allowed, using glob ... - * to allow splitting per ext (extbase, fluid, ...) and TYPO3 Version 7.1, 7.0, ... - */ - public function __construct() + protected function getRemovedConfigFiles() { - if (static::$removedConstants === []) { - foreach ($this->getRemovedConstantConfigFiles() as $file) { - static::$removedConstants = array_merge( - static::$removedConstants, - $this->prepareStructure(Yaml::parse(file_get_contents((string) $file))) - ); - } - } + return $this->getRemovedConstantConfigFiles(); } /** * Prepares structure from config for later usage. * - * @param array $oldStructure + * @param array $typo3Versions * @return array */ - protected function prepareStructure(array $oldStructure) - { - $typo3Versions = array_keys($oldStructure); - $newStructure = []; + // protected function prepareStructure(array $typo3Versions) + // { + // $newStructure = []; - foreach ($typo3Versions as $typo3Version) { - foreach ($oldStructure[$typo3Version] as $constant => $config) { - // Split static methods and methods. - $split = preg_split('/::|->/', $constant); + // foreach ($typo3Versions as $typo3Version => $constants) { + // foreach ($constants as $constant => $config) { + // $split = explode('::', $constant); - $newStructure[$constant] = $config; + // $newStructure[$constant] = $config; - $newStructure[$constant]['static'] = strpos($constant, '::') !== false; - $newStructure[$constant]['fqcn'] = null; - $newStructure[$constant]['class'] = null; - $newStructure[$constant]['constant'] = $split[0]; - $newStructure[$constant]['version_removed'] = $typo3Version; + // $newStructure[$constant]['static'] = strpos($constant, '::') !== false; + // $newStructure[$constant]['fqcn'] = null; + // $newStructure[$constant]['class'] = null; + // $newStructure[$constant]['constant'] = $constant; + // $newStructure[$constant]['version_removed'] = $typo3Version; + // // TODO: Handle constants of classes + // }; + // } - // If split contains two parts, it's a class with method - if (isset($split[1])) { - $newStructure[$constant]['fqcn'] = $split[0]; - $newStructure[$constant]['class'] = array_slice( - explode('\\', $newStructure[$constant]['fqcn']), - -1 - )[0]; - $newStructure[$constant]['constant'] = $split[1]; - } - }; - } - - return $newStructure; - } + // return $newStructure; + // } /** * Returns the token types that this sniff is interested in. @@ -107,144 +75,7 @@ class Typo3Update_Sniffs_Removed_GenericConstantUsageCallSniff implements PhpCsS */ public function register() { - return Tokens::$constantNameTokens; - } - - /** - * Processes the tokens that this sniff is interested in. - * - * This is the default implementation, as most of the time next T_STRING is - * the class name. This way only the register method has to be registered - * in default cases. - * - * @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) - { - if (!$this->isConstantCallRemoved($phpcsFile, $stackPtr)) { - return; - } - - $this->addWarning($phpcsFile, $stackPtr); - } - - /** - * Check whether constant at given point is removed. - * - * @return bool - */ - protected function isConstantCallRemoved(PhpCsFile $phpcsFile, $stackPtr) - { - if (!$this->isConstantCall($phpcsFile, $stackPtr)) { - return false; - } - - $tokens = $phpcsFile->getTokens(); - $staticPosition = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true); - - $constantName = $tokens[$stackPtr]['content']; - $isStatic = false; - $class = false; - - if ($staticPosition !== false) { - $isStatic = $tokens[$staticPosition]['code'] === T_DOUBLE_COLON; - } - - if ($isStatic) { - $class = $phpcsFile->findPrevious(T_STRING, $staticPosition, null, false, null, true); - if ($class !== false) { - $class = $tokens[$class]['content']; - } - } - - return $this->getRemovedConstant($constantName, $class, $isStatic) !== []; - } - - /** - * Returns all matching removed constants for given arguments. - * - * Also prepares constants for later usages in $this->removedConstant. - * - * @param string $constantName - * @param string $className The last part of the class name, splitted by namespaces. - * @param bool $isStatic - * - * @return array - */ - protected function getRemovedConstant($constantName, $className, $isStatic) - { - // We will not match any static method, without the class name, at least for now. - // Otherwise we could handle them the same way as instance methods. - if ($isStatic === true && $className === false) { - return []; - } - - $this->removedConstant = array_filter( - static::$removedConstants, - function ($config) use ($constantName, $isStatic, $className) { - return $constantName === $config['constant'] - && $isStatic === $config['static'] - && ( - $className === $config['class'] - || $className === false - ) - ; - } - ); - - return $this->removedConstant; - } - - /** - * Returns configuration for currently checked constant. - * - * @return array - */ - protected function getCurrentRemovedConstant() - { - $config = current($this->removedConstant); - - // TODO: Add exception if something went wrong? - - return $config; - } - - /** - * Add warning for the given token position. - * - * @param PhpCsFile $phpcsFile - * @param int $tokenPosition - * - * @return void - */ - protected function addWarning(PhpCsFile $phpcsFile, $tokenPosition) - { - $phpcsFile->addWarning( - 'Legacy constant calls are not allowed; found %s. Removed in %s. %s. See: %s', - $tokenPosition, - $this->getConstantIdentifier(), - [ - $this->getOldConstantCall(), - $this->getRemovedVersion(), - $this->getNewConstantCall(), - $this->getDocsUrl(), - ] - ); - } - - /** - * Identifier for configuring this specific error / warning through PHPCS. - * - * @return string - */ - protected function getConstantIdentifier() - { - $config = $this->getCurrentRemovedConstant(); - return $config['class'] . '.' . $config['constant']; + return [T_STRING]; } /** @@ -254,48 +85,17 @@ class Typo3Update_Sniffs_Removed_GenericConstantUsageCallSniff implements PhpCsS * you should provide an example, so users can check that this is the * legacy one. * - * @return string - */ - protected function getOldConstantCall() - { - return $this->getCurrentRemovedConstant(); - } - - /** - * Returns TYPO3 version when the current constant was removed. - * - * To let user decide whether this is important for him. + * @param array $config The converted structure for a single constant. * * @return string */ - protected function getRemovedVersion() + protected function getOldUsage(array $config) { - return $this->getCurrentRemovedConstant()['version_removed']; - } - - /** - * The new function call, or information how to migrate. - * - * To provide feedback for user to ease migration. - * - * @return string - */ - protected function getNewConstantCall() - { - $newCall = $this->getCurrentRemovedConstant()['newConstantCall']; - if ($newCall !== null) { - return $newCall; + $old = $config['name']; + if ($config['static']) { + $old = $config['fqcn'] . '::' . $config['name']; } - return 'There is no replacement, just remove call'; - } - /** - * Allow user to lookup the official docs related to this deprecation / breaking change. - * - * @return string - */ - protected function getDocsUrl() - { - return $this->getCurrentRemovedConstant()['docsUrl']; + return 'constant ' . $old; } } diff --git a/src/Standards/Typo3Update/Sniffs/Removed/GenericFunctionCallSniff.php b/src/Standards/Typo3Update/Sniffs/Removed/GenericFunctionCallSniff.php index b81ff7d..9d9b30a 100644 --- a/src/Standards/Typo3Update/Sniffs/Removed/GenericFunctionCallSniff.php +++ b/src/Standards/Typo3Update/Sniffs/Removed/GenericFunctionCallSniff.php @@ -20,9 +20,8 @@ */ use PHP_CodeSniffer_File as PhpCsFile; -use PHP_CodeSniffer_Sniff as PhpCsSniff; use PHP_CodeSniffer_Tokens as Tokens; -use Symfony\Component\Yaml\Yaml; +use Typo3Update\Sniffs\Removed\AbstractGenericUsage; /** * Sniff that handles all calls to removed functions. @@ -30,76 +29,16 @@ use Symfony\Component\Yaml\Yaml; * Removed functions are configured using YAML-Files, for examples see src/Standards/Typo3Update/Configuration/Removed/Functions/7.0.yaml * Also check out the configuration options in Readme.rst. */ -class Typo3Update_Sniffs_Removed_GenericFunctionCallSniff implements PhpCsSniff +class Typo3Update_Sniffs_Removed_GenericFunctionCallSniff extends AbstractGenericUsage { - use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; - use \Typo3Update\Sniffs\OptionsAccessTrait; - /** - * Configuration to define removed functions. + * Return file names containing removed configurations. * - * @var array + * @return array */ - protected static $configuredFunctions = []; - - /** - * Function for the current sniff instance. - * @var array - */ - private $removedFunctions = []; - - /** - * TODO: Multiple files allowed, using glob ... - * to allow splitting per ext (extbase, fluid, ...) and TYPO3 Version 7.1, 7.0, ... - */ - public function __construct() + protected function getRemovedConfigFiles() { - if (static::$configuredFunctions === []) { - foreach ($this->getRemovedFunctionConfigFiles() as $file) { - static::$configuredFunctions = array_merge( - static::$configuredFunctions, - $this->prepareStructure(Yaml::parse(file_get_contents((string) $file))) - ); - } - } - } - - /** - * Prepares structure from config for later usage. - * - * @param array $typo3Versions - * @return array - */ - protected function prepareStructure(array $typo3Versions) - { - $newStructure = []; - - foreach ($typo3Versions as $typo3Version => $functions) { - foreach ($functions as $function => $config) { - // Split static methods and methods. - $split = preg_split('/::|->/', $function); - - $newStructure[$function] = $config; - - $newStructure[$function]['static'] = strpos($function, '::') !== false; - $newStructure[$function]['fqcn'] = null; - $newStructure[$function]['class'] = null; - $newStructure[$function]['function'] = $split[0]; - $newStructure[$function]['version_removed'] = $typo3Version; - - // If split contains two parts, it's a class with method - if (isset($split[1])) { - $newStructure[$function]['fqcn'] = $split[0]; - $newStructure[$function]['class'] = array_slice( - explode('\\', $newStructure[$function]['fqcn']), - -1 - )[0]; - $newStructure[$function]['function'] = $split[1]; - } - }; - } - - return $newStructure; + return $this->getRemovedFunctionConfigFiles(); } /** @@ -112,127 +51,18 @@ class Typo3Update_Sniffs_Removed_GenericFunctionCallSniff implements PhpCsSniff return Tokens::$functionNameTokens; } - /** - * Processes the tokens that this sniff is interested in. - * - * This is the default implementation, as most of the time next T_STRING is - * the class name. This way only the register method has to be registered - * in default cases. - * - * @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) - { - if (!$this->isFunctionCallRemoved($phpcsFile, $stackPtr)) { - return; - } - - $this->addWarning($phpcsFile, $stackPtr); - } - /** * Check whether function at given point is removed. * * @return bool */ - protected function isFunctionCallRemoved(PhpCsFile $phpcsFile, $stackPtr) + protected function isRemoved(PhpCsFile $phpcsFile, $stackPtr) { if (!$this->isFunctionCall($phpcsFile, $stackPtr)) { return false; } - $tokens = $phpcsFile->getTokens(); - $staticPosition = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true); - - $functionName = $tokens[$stackPtr]['content']; - $isStatic = false; - $class = false; - - if ($staticPosition !== false) { - $isStatic = $tokens[$staticPosition]['code'] === T_DOUBLE_COLON; - } - - if ($isStatic) { - $class = $phpcsFile->findPrevious(T_STRING, $staticPosition, null, false, null, true); - if ($class !== false) { - $class = $tokens[$class]['content']; - } - } - - $this->removedFunctions = $this->getMatchingRemovedFunctions($functionName, $class, $isStatic); - return $this->removedFunctions !== []; - } - - /** - * Returns all matching removed functions for given arguments. - * - * @param string $functionName - * @param string $className The last part of the class name, splitted by namespaces. - * @param bool $isStatic - * - * @return void - */ - protected function getMatchingRemovedFunctions($functionName, $className, $isStatic) - { - // We will not match any static method, without the class name, at least for now. - // Otherwise we could handle them the same way as instance methods. - if ($isStatic === true && $className === false) { - return; - } - - return array_filter( - static::$configuredFunctions, - function ($config) use ($functionName, $isStatic, $className) { - return $functionName === $config['function'] - && $isStatic === $config['static'] - && ( - $className === $config['class'] - || $className === false - ) - ; - } - ); - } - - /** - * Add warning for the given token position. - * - * @param PhpCsFile $phpcsFile - * @param int $tokenPosition - * - * @return void - */ - protected function addWarning(PhpCsFile $phpcsFile, $tokenPosition) - { - foreach ($this->removedFunctions as $function) { - $phpcsFile->addWarning( - 'Legacy function calls are not allowed; found %s. Removed in %s. %s. See: %s', - $tokenPosition, - $this->getFunctionIdentifier($function), - [ - $this->getOldfunctionCall($function), - $this->getRemovedVersion($function), - $this->getNewFunctionCall($function), - $this->getDocsUrl($function), - ] - ); - } - } - - /** - * Identifier for configuring this specific error / warning through PHPCS. - * - * @param array $config The converted structure for a single function. - * - * @return string - */ - protected function getFunctionIdentifier(array $config) - { - return $config['class'] . '.' . $config['function']; + parent::isRemoved($phpcsFile, $stackPtr); } /** @@ -246,56 +76,12 @@ class Typo3Update_Sniffs_Removed_GenericFunctionCallSniff implements PhpCsSniff * * @return string */ - protected function getOldFunctionCall(array $config) + protected function getOldUsage(array $config) { $concat = '->'; if ($config['static']) { $concat = '::'; } - return $config['fqcn'] . $concat . $config['function']; - } - - /** - * Returns TYPO3 version when the current function was removed. - * - * To let user decide whether this is important for him. - * - * @param array $config The converted structure for a single function. - * - * @return string - */ - protected function getRemovedVersion(array $config) - { - return $config['version_removed']; - } - - /** - * The new function call, or information how to migrate. - * - * To provide feedback for user to ease migration. - * - * @param array $config The converted structure for a single function. - * - * @return string - */ - protected function getNewFunctionCall(array $config) - { - $newCall = $config['newFunctionCall']; - if ($newCall !== null) { - return $newCall; - } - return 'There is no replacement, just remove call'; - } - - /** - * Allow user to lookup the official docs related to this deprecation / breaking change. - * - * @param array $config The converted structure for a single function. - * - * @return string - */ - protected function getDocsUrl(array $config) - { - return $config['docsUrl']; + return $config['fqcn'] . $concat . $config['name']; } }