diff --git a/composer.json b/composer.json index 6f9a5df..87aab93 100644 --- a/composer.json +++ b/composer.json @@ -23,6 +23,7 @@ ] }, "require": { + "php": "~7.1", "helmich/typo3-typoscript-parser": "1.1.*", "squizlabs/php_codesniffer": "2.8.*", "symfony/yaml": "3.2.*", diff --git a/src/Standards/Typo3Update/Feature/RemovedByYamlConfiguration.php b/src/Standards/Typo3Update/Feature/RemovedByYamlConfiguration.php deleted file mode 100644 index 53f28a5..0000000 --- a/src/Standards/Typo3Update/Feature/RemovedByYamlConfiguration.php +++ /dev/null @@ -1,294 +0,0 @@ - - * - * 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; -use Typo3Update\Options; - -/** - * 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; - - /** - * Configuration to define removed code. - * - * @var array - */ - protected $configured = []; - - /** - * Constant for the current sniff instance. - * @var array - */ - protected $removed = []; - - 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 - 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->addMessage($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 calls, without the class name, at least for now. - 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 message for the given token position. - * - * Default is a warning, non fixable. Just overwrite in concrete sniff, if - * something different suites better. - * - * @param PhpCsFile $phpcsFile - * @param int $tokenPosition - * - * @return void - */ - protected function addMessage(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 - * - * @return string - */ - protected function getIdentifier(array $config) - { - $name = $config['name']; - if ($config['class']) { - $name = $config['class'] . '.' . $name; - } - - return $name; - } - - /** - * The original call, to allow user to check matches. - * - * As we match the 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 - * - * @return string - */ - abstract protected function getOldUsage(array $config); - - /** - * Returns TYPO3 version when the breaking change happened. - * - * To let user decide whether this is important for him. - * - * @param array $config - * - * @return string - */ - protected function getRemovedVersion(array $config) - { - return $config['version_removed']; - } - - /** - * The new call, or information how to migrate. - * - * To provide feedback for user to ease migration. - * - * @param array $config - * - * @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/RemovedByYamlConfiguration.php b/src/Standards/Typo3Update/RemovedByYamlConfiguration.php new file mode 100644 index 0000000..f1ab402 --- /dev/null +++ b/src/Standards/Typo3Update/RemovedByYamlConfiguration.php @@ -0,0 +1,60 @@ + + * + * 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 Symfony\Component\Yaml\Yaml; + +class RemovedByYamlConfiguration +{ + /** + * Configuration to define removed code. + * + * @var array + */ + protected $configured = []; + + public function __construct(array $configFiles, $prepareStructure) + { + foreach ($configFiles as $file) { + $this->configured = array_merge( + $this->configured, + $prepareStructure(Yaml::parse(file_get_contents((string) $file))) + ); + } + } + + public function isRemoved($identifier) + { + return isset($this->configured[$identifier]); + } + + public function getRemoved($identifier) + { + if (!$this->isRemoved($identifier)) { + throw new \Exception( + sprintf('Identifier "%s" is not configured to be removed.', $identifier), + 1493289133 + ); + } + + return $this->configured[$identifier]; + } +} diff --git a/src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php b/src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php index 3cda85a..f8e8e0a 100644 --- a/src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php +++ b/src/Standards/Typo3Update/Sniffs/Removed/AbstractGenericUsage.php @@ -22,9 +22,7 @@ namespace Typo3Update\Sniffs\Removed; 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\Options; +use Typo3Update\RemovedByYamlConfiguration; /** * Contains common functionality for removed code like constants or functions. @@ -35,33 +33,24 @@ use Typo3Update\Options; */ abstract class AbstractGenericUsage implements PhpCsSniff { - use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; - - /** - * Configuration to define removed code. - * - * @var array - */ - protected $configured = []; - - /** - * Entries removed in current sniff. - * @var array - */ - protected $removed = []; + protected $configured; 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))) - ); - } - } + $this->configured = new RemovedByYamlConfiguration( + $this->getRemovedConfigFiles(), + \Closure::fromCallable([$this, 'prepareStructure']) + ); } + /** + * Prepares structure from config for later usage. + * + * @param array $typo3Versions + * @return array + */ + abstract protected function prepareStructure(array $typo3Versions); + /** * Return file names containing removed configurations. * @@ -69,199 +58,35 @@ abstract class AbstractGenericUsage implements PhpCsSniff */ abstract protected function getRemovedConfigFiles(); - /** - * Prepares structure from config for later usage. - * - * @param array $typo3Versions - * @return array - */ - protected function prepareStructure(array $typo3Versions) - { - $newStructure = []; + abstract protected function findRemoved(PhpCsFile $phpcsFile, $stackPtr); - 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 - 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)) { + $removed = $this->findRemoved($phpcsFile, $stackPtr); + if ($removed === []) { return; } - $this->addMessage($phpcsFile, $stackPtr); + $this->addMessage($removed); } - /** - * Check whether the current token is removed. - * - * @param PhpCsFile $phpcsFile - * @param int $stackPtr - * @return bool - */ - protected function isRemoved(PhpCsFile $phpcsFile, $stackPtr) + protected function addMessage(array $removed) { - $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 calls, without the class name, at least for now. - 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 message for the given token position. - * - * Default is a warning, non fixable. Just overwrite in concrete sniff, if - * something different suites better. - * - * @param PhpCsFile $phpcsFile - * @param int $tokenPosition - * - * @return void - */ - protected function addMessage(PhpCsFile $phpcsFile, $tokenPosition) - { - foreach ($this->removed as $removed) { + foreach ($removed as $removed) { $phpcsFile->addWarning( - 'Legacy calls are not allowed; found %s. Removed in %s. %s. See: %s', + 'Calls to removed code are not allowed; found %s. Removed in %s. %s. See: %s', $tokenPosition, - $this->getIdentifier($removed), + $removed['identifier'], [ - $this->getOldUsage($removed), - $this->getRemovedVersion($removed), + $removed['oldUsage'], + $removed['versionRemoved'], $this->getReplacement($removed), - $this->getDocsUrl($removed), + $removed['docsUrl'], ] ); } } - /** - * Identifier for configuring this specific error / warning through PHPCS. - * - * @param array $config - * - * @return string - */ - protected function getIdentifier(array $config) - { - $name = $config['name']; - if ($config['class']) { - $name = $config['class'] . '.' . $name; - } - - return $name; - } - - /** - * The original call, to allow user to check matches. - * - * As we match the 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 - * - * @return string - */ - abstract protected function getOldUsage(array $config); - - /** - * Returns TYPO3 version when the breaking change happened. - * - * To let user decide whether this is important for him. - * - * @param array $config - * - * @return string - */ - protected function getRemovedVersion(array $config) - { - return $config['version_removed']; - } - /** * The new call, or information how to migrate. * @@ -279,16 +104,4 @@ abstract class AbstractGenericUsage implements PhpCsSniff } return 'There is no replacement, just remove call'; } - - /** - * Allow user to lookup the official docs related to this deprecation / breaking change. - * - * @param array $config - * - * @return string - */ - protected function getDocsUrl(array $config) - { - return $config['docsUrl']; - } } diff --git a/src/Standards/Typo3Update/Sniffs/Removed/TypoScriptSniff.php b/src/Standards/Typo3Update/Sniffs/Removed/TypoScriptSniff.php index 98c0cca..739f21a 100644 --- a/src/Standards/Typo3Update/Sniffs/Removed/TypoScriptSniff.php +++ b/src/Standards/Typo3Update/Sniffs/Removed/TypoScriptSniff.php @@ -59,7 +59,6 @@ class Typo3Update_Sniffs_Removed_TypoScriptSniff extends AbstractGenericUsage protected function prepareStructure(array $typo3Versions) { $newStructure = []; - foreach ($typo3Versions as $typo3Version => $removals) { foreach ($removals as $removed => $config) { $config['type'] = TokenInterface::TYPE_OBJECT_IDENTIFIER; @@ -70,7 +69,9 @@ class Typo3Update_Sniffs_Removed_TypoScriptSniff extends AbstractGenericUsage } $config['name'] = $removed; - $config['version_removed'] = $typo3Version; + $config['identifier'] = str_replace('.', '-', $removed); + $config['versionRemoved'] = $typo3Version; + $config['oldUsage'] = $removed; $newStructure[$removed] = $config; } @@ -86,48 +87,20 @@ class Typo3Update_Sniffs_Removed_TypoScriptSniff extends AbstractGenericUsage * @param int $stackPtr * @return bool */ - protected function isRemoved(PhpCsFile $phpcsFile, $stackPtr) + protected function findRemoved(PhpCsFile $phpcsFile, $stackPtr) { $tokens = $phpcsFile->getTokens(); $token = $tokens[$stackPtr]; $objectIdentifier = $token['content']; - if (isset($this->configured[$objectIdentifier]) && $token['type'] === $this->configured[$objectIdentifier]['type']) { - $this->removed = [ - $this->configured[$objectIdentifier] - ]; - return true; + if (!$this->configured->isRemoved($objectIdentifier)) { + return []; } - return false; - } - - /** - * Identifier for configuring this specific error / warning through PHPCS. - * - * @param array $config - * - * @return string - */ - protected function getIdentifier(array $config) - { - return str_replace('.', '-', $config['name']); - } - - /** - * The original call, to allow user to check matches. - * - * As we match the 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 - * - * @return string - */ - protected function getOldUsage(array $config) - { - return $config['name']; + $removed = $this->configured->getRemoved($objectIdentifier); + if ($token['type'] === $removed['type']) { + return [$removed]; + } } /**