FEATURE: Provide code to handle deprecated method calls

* Add parsing of YAML-files.
* Check matching deprecated calls.
* Provide necessary information for PHPCS and user.

Relates: #33
This commit is contained in:
Daniel Siepmann 2017-03-28 15:29:26 +02:00
parent cd434ac639
commit b652137e96
Signed by: Daniel Siepmann
GPG key ID: 33D6629915560EF4
2 changed files with 205 additions and 134 deletions

View file

@ -1,114 +1,85 @@
# Deprecated in 7.0: https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Index.html#breaking-changes
loadTCA:
oldFunctionCall: '\TYPO3\CMS\Core\Utility\GeneralUtility::loadTCA()'
\TYPO3\CMS\Core\Utility\GeneralUtility::loadTCA:
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61785-LoadTcaFunctionRemoved.html'
getCompressedTCarray:
oldFunctionCall: '\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::getCompressedTCarray()'
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->getCompressedTCarray:
newFunctionCall: 'Full TCA is always loaded during bootstrap in FE, the method is obsolete. If an eid script calls this method to load TCA, use \TYPO3\CMS\Frontend\Utility\EidUtility::initTCA() instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61785-FrontendTcaFunctionsRemoved.html'
includeTCA:
oldFunctionCall: '\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::includeTCA()'
\TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController->includeTCA:
newFunctionCall: 'Full TCA is always loaded during bootstrap in FE, the method is obsolete. If an eid script calls this method to load TCA, use \TYPO3\CMS\Frontend\Utility\EidUtility::initTCA() instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61785-FrontendTcaFunctionsRemoved.html'
mail:
oldFunctionCall: 'MailUtility::mail()'
\TYPO3\CMS\Core\Utility\MailUtility::mail:
newFunctionCall: 'Use the \TYPO3\CMS\Core\Mail\Mailer API for sending email'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61783-RemoveDeprecatedMailFunctionality.html'
plainMailEncoded:
oldFunctionCall: 'GeneralUtility::plainMailEncoded()'
\TYPO3\CMS\Core\Utility\GeneralUtility::plainMailEncoded:
newFunctionCall: 'Use the \TYPO3\CMS\Core\Mail\Mailer API for sending email'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61783-RemoveDeprecatedMailFunctionality.html'
connectDB:
oldFunctionCall: '\TYPO3\CMS\Frontend\Utility\EidUtility::connectDB()'
\TYPO3\CMS\Frontend\Utility\EidUtility::connectDB:
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61863-ConnectDbFunctionRemoved.html'
int_from_ver:
oldFunctionCall: '\TYPO3\CMS\Core\Utility\GeneralUtility::int_from_ver'
newFunctionCall: 'Replace the usage of the removed function with \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger()'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61860-RemoveIntFromVerFunction.html'
getUniqueFields:
oldFunctionCall: '\TYPO3\CMS\Core\DataHandling\DataHandler::getUniqueFields()'
newFunctionCall: 'Replace all calls to \TYPO3\CMS\Core\DataHandling\DataHandler::getUniqueFields() with calls to \TYPO3\CMS\Version\Hook\DataHandlerHook::getUniqueFields()'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61822-GetUniqueFieldsFunctionRemoved.html'
isSafeModeEnabled:
oldFunctionCall: '\TYPO3\CMS\Core\Utility\PhpOptionsUtility::isSafeModeEnabled()'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61820-PhpOptionsUtilityDeprecatedFunctionsRemoved.html'
isMagicQuotesGpcEnabled:
oldFunctionCall: '\TYPO3\CMS\Core\Utility\PhpOptionsUtility::isMagicQuotesGpcEnabled()'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61820-PhpOptionsUtilityDeprecatedFunctionsRemoved.html'
isLocalconfWritable:
oldFunctionCall: '\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::isLocalconfWritable'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61802-IsLocalconfWritableFunctionRemoved.html'
create:
oldFunctionCall: 'ObjectManager::create()'
newFunctionCall: 'Use ObjectManager::get() instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
# TODO: Find a way to make same method name in different classes work.
# create:
# oldFunctionCall: 'ObjectManagerInterface::create()'
# int_from_ver:
# newFunctionCall: 'Replace the usage of the removed function with \TYPO3\CMS\Core\Utility\VersionNumberUtility::convertVersionNumberToInteger()'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61860-RemoveIntFromVerFunction.html'
# getUniqueFields:
# newFunctionCall: 'Replace all calls to \TYPO3\CMS\Core\DataHandling\DataHandler::getUniqueFields() with calls to \TYPO3\CMS\Version\Hook\DataHandlerHook::getUniqueFields()'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61822-GetUniqueFieldsFunctionRemoved.html'
# isSafeModeEnabled:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61820-PhpOptionsUtilityDeprecatedFunctionsRemoved.html'
# isMagicQuotesGpcEnabled:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61820-PhpOptionsUtilityDeprecatedFunctionsRemoved.html'
# isLocalconfWritable:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-61802-IsLocalconfWritableFunctionRemoved.html'
# create:
# newFunctionCall: 'Use ObjectManager::get() instead'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
replaceObject:
oldFunctionCall: 'PersistenceGenericBackend::replaceObject()'
newFunctionCall: 'Removed without replacement'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
setReturnRawQueryResult:
oldFunctionCall: 'QuerySettingsInterface::setReturnRawQueryResult()'
newFunctionCall: 'Removed without replacement'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
getReturnRawQueryResult:
oldFunctionCall: 'QuerySettingsInterface::getReturnRawQueryResult()'
newFunctionCall: 'Use the parameter on $query->execute() directly'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
setSysLanguageUid:
oldFunctionCall: 'Typo3QuerySettings::setSysLanguageUid()'
newFunctionCall: 'Use setLanguageUid() instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
getSysLanguageUid:
oldFunctionCall: 'Typo3QuerySettings::getSysLanguageUid()'
newFunctionCall: 'Use getLanguageUid() instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
JScharCode:
oldFunctionCall: 'LanguageService::JScharCode()'
newFunctionCall: 'Use GeneralUtility::quoteJSvalue instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
joinTSarrays:
oldFunctionCall: 'ContentObjectRenderer::joinTSarrays()'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
tidyHTML:
oldFunctionCall: 'TypoScriptFrontendController::tidyHTML()'
newFunctionCall: 'You may use the tidy extension from TER'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
isWebFolder:
oldFunctionCall: 'ElementBrowser::isWebFolder()'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
checkFolder:
oldFunctionCall: 'ElementBrowser::checkFolder()'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
getTreeObject:
oldFunctionCall: 'AbstractDatabaseRecordList::getTreeObject()'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
dirData:
oldFunctionCall: 'FileList::dirData()'
newFunctionCall: null
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
stdWrapValue:
oldFunctionCall: 'FilesContentObject::stdWrapValue()'
newFunctionCall: 'Use ContentObjectRenderer::stdWrapValue instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
userTempFolder:
oldFunctionCall: 'ImportExportController::userTempFolder()'
newFunctionCall: 'Use getDefaultImportExportFolder instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
userSaveFolder:
oldFunctionCall: 'ImportExportController::userSaveFolder()'
newFunctionCall: 'Use getDefaultImportExportFolder instead'
docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# # create:
# # newFunctionCall: null
# # docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
# replaceObject:
# newFunctionCall: 'Removed without replacement'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
# setReturnRawQueryResult:
# newFunctionCall: 'Removed without replacement'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
# getReturnRawQueryResult:
# newFunctionCall: 'Use the parameter on $query->execute() directly'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
# setSysLanguageUid:
# newFunctionCall: 'Use setLanguageUid() instead'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
# getSysLanguageUid:
# newFunctionCall: 'Use getLanguageUid() instead'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62673-ExtbaseDeprecatedCodeRemoved.html'
# JScharCode:
# newFunctionCall: 'Use GeneralUtility::quoteJSvalue instead'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# joinTSarrays:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# tidyHTML:
# newFunctionCall: 'You may use the tidy extension from TER'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# isWebFolder:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# checkFolder:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# getTreeObject:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# dirData:
# newFunctionCall: null
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# stdWrapValue:
# newFunctionCall: 'Use ContentObjectRenderer::stdWrapValue instead'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# userTempFolder:
# newFunctionCall: 'Use getDefaultImportExportFolder instead'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'
# userSaveFolder:
# newFunctionCall: 'Use getDefaultImportExportFolder instead'
# docsUrl: 'https://docs.typo3.org/typo3cms/extensions/core/Changelog/7.0/Breaking-62670-DeprecatedCodeRemovalInMultipleSysexts.html'

View file

@ -37,27 +37,59 @@ class Typo3Update_Sniffs_Deprecated_GenericFunctionCallSniff implements PhpCsSni
/**
* Configuration to define deprecated functions.
*
* Keys have to match the function name.
*
* TODO: Multiple files allowed, using glob ... to allow splitting per ext (extbase, fluid, ...) and TYPO3 Version 7.1, 7.0, ...
* TODO: Build necessary structure from that files, to make it more independent ... ?!
*
* @var array
*/
protected static $deprecatedFunctions = [];
/**
* Function for the current sniff instance.
* @var array
*/
private $deprecatedFunction = [];
/**
* TODO: Multiple files allowed, using glob ... to allow splitting per ext (extbase, fluid, ...) and TYPO3 Version 7.1, 7.0, ...
*/
public function __construct()
{
if (static::$deprecatedFunctions === []) {
foreach ($this->getDeprecatedFunctionConfigFiles() as $file) {
static::$deprecatedFunctions = array_merge(
static::$deprecatedFunctions,
Yaml::parse(file_get_contents((string) $file))
$this->prepareStructure(Yaml::parse(file_get_contents((string) $file)))
);
}
}
}
/**
* Prepares structure from config for later usage.
*
* @param array $deprecatedFunctions
* @return array
*/
protected function prepareStructure(array $deprecatedFunctions)
{
array_walk($deprecatedFunctions, function (&$config, $function) {
// Split static methods and methods.
$split = preg_split('/::|->/', $function);
$config['static'] = strpos($function, '::') !== false;
$config['fqcn'] = null;
$config['class'] = null;
$config['function'] = $split[0];
// If split contains two parts, it's a class with method
if (isset($split[1])) {
$config['fqcn'] = $split[0];
$config['class'] = array_slice(explode('\\', $config['fqcn']), -1)[0];
$config['function'] = $split[1];
}
});
return $deprecatedFunctions;
}
/**
* Returns the token types that this sniff is interested in.
*
@ -83,25 +115,92 @@ class Typo3Update_Sniffs_Deprecated_GenericFunctionCallSniff implements PhpCsSni
*/
public function process(PhpCsFile $phpcsFile, $stackPtr)
{
if (!$this->isFunctionCall($phpcsFile, $stackPtr)) {
if (!$this->isFunctionCallDeprecated($phpcsFile, $stackPtr)) {
return;
}
$this->addWarning($phpcsFile, $stackPtr);
}
/**
* Check whether function at given point is deprecated.
*
* @return bool
*/
protected function isFunctionCallDeprecated(PhpCsFile $phpcsFile, $stackPtr)
{
if (!$this->isFunctionCall($phpcsFile, $stackPtr)) {
return false;
}
$tokens = $phpcsFile->getTokens();
$token = $tokens[$stackPtr];
$staticPosition = $phpcsFile->findPrevious(T_WHITESPACE, $stackPtr - 1, null, true, null, true);
if (in_array($token['content'], $this->getFunctionNames()) === false) {
return;
$functionName = $tokens[$stackPtr]['content'];
$isStatic = false;
$class = false;
if ($staticPosition !== false) {
$isStatic = $tokens[$staticPosition]['code'] === T_DOUBLE_COLON;
}
// TODO: Check if function is static and whether last class name part matches.
// TODO: Add new property to array "last class name part" and use for check if exists.
// TODO: How to handle methods? They are not static, are behind a variable or something else ...
if ($isStatic) {
$class = $phpcsFile->findPrevious(T_STRING, $staticPosition, null, false, null, true);
if ($class !== false) {
$class = $tokens[$class]['content'];
}
}
// E.g.: getUniqueFields
// E.g.: mail
return $this->getDeprecatedFunction($functionName, $class, $isStatic) !== [];
}
$this->addWarning($phpcsFile, $stackPtr);
/**
* Returns all matching deprecated functions for given arguments.
*
* Also prepares functions for later usages in $this->deprecatedFunction.
*
* @param string $functionName
* @param string $className The last part of the class name, splitted by namespaces.
* @param bool $isStatic
*
* @return array
*/
protected function getDeprecatedFunction($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 [];
}
$this->deprecatedFunction = array_filter(
static::$deprecatedFunctions,
function ($config) use ($functionName, $isStatic, $className) {
return $functionName === $config['function']
&& $isStatic === $config['static']
&& (
$className === $config['class']
|| $className === false
) // TODO: If no class, it's also fine, vor variable, non static methods.
;
}
);
return $this->deprecatedFunction;
}
/**
* Returns configuration for currently checked function.
*
* @return array
*/
protected function getCurrentDeprecatedFunction()
{
$config = current($this->deprecatedFunction);
// TODO: Add exception if something went wrong?
return $config;
}
/**
@ -114,31 +213,27 @@ class Typo3Update_Sniffs_Deprecated_GenericFunctionCallSniff implements PhpCsSni
*/
protected function addWarning(PhpCsFile $phpcsFile, $tokenPosition)
{
$tokens = $phpcsFile->getTokens();
$token = $tokens[$tokenPosition];
$functionCall = $token['content'];
$phpcsFile->addWarning(
'Legacy function calls are not allowed; found %s. %s. See: %s',
$tokenPosition,
$functionCall,
$this->getFunctionIdentifier(),
[
$this->getOldFunctionCall($functionCall),
$this->getNewFunctionCall($functionCall),
$this->getDocsUrl($functionCall),
$this->getOldfunctionCall(),
$this->getNewFunctionCall(),
$this->getDocsUrl(),
]
);
}
/**
* Provide all function names that are deprecated and should be handled by
* the Sniff.
* Identifier for configuring this specific error / warning through PHPCS.
*
* @return array
* @return string
*/
protected function getFunctionNames()
protected function getFunctionIdentifier()
{
return array_keys(static::$deprecatedFunctions);
$config = $this->getCurrentDeprecatedFunction();
return $config['class'] . '.' . $config['function'];
}
/**
@ -150,9 +245,14 @@ class Typo3Update_Sniffs_Deprecated_GenericFunctionCallSniff implements PhpCsSni
*
* @return string
*/
protected function getOldFunctionCall($functionName)
protected function getOldFunctionCall()
{
return static::$deprecatedFunctions[$functionName]['oldFunctionCall'];
$config = $this->getCurrentDeprecatedFunction();
$concat = '->';
if ($config['static']) {
$concat = '::';
}
return $config['fqcn'] . $concat . $config['function'];
}
/**
@ -162,9 +262,9 @@ class Typo3Update_Sniffs_Deprecated_GenericFunctionCallSniff implements PhpCsSni
*
* @return string
*/
protected function getNewFunctionCall($functionName)
protected function getNewFunctionCall()
{
$newCall = static::$deprecatedFunctions[$functionName]['newFunctionCall'];
$newCall = $this->getCurrentDeprecatedFunction()['newFunctionCall'];
if ($newCall !== null) {
return $newCall;
}
@ -176,8 +276,8 @@ class Typo3Update_Sniffs_Deprecated_GenericFunctionCallSniff implements PhpCsSni
*
* @return string
*/
protected function getDocsUrl($functionName)
protected function getDocsUrl()
{
return static::$deprecatedFunctions[$functionName]['docsUrl'];
return $this->getCurrentDeprecatedFunction()['docsUrl'];
}
}