Merge pull request #39 from DanielSiepmann/feature/36-namespace-migration

Feature/36 namespace migration
This commit is contained in:
Daniel Hürtgen 2017-03-30 16:11:23 +02:00 committed by GitHub
commit 2d33832a79
19 changed files with 652 additions and 101 deletions

View file

@ -49,6 +49,8 @@ new ones like ``\TYPO3\Extbase\...``. This is done for:
- Static calls like ``t3lib_div::`` to ``\TYPO3\Core\Utility\GeneralUtility``. - Static calls like ``t3lib_div::`` to ``\TYPO3\Core\Utility\GeneralUtility``.
- Static call also checks for ``::class``, as technically we just look before the ``::``.
- Typehints in methods and function like injects. - Typehints in methods and function like injects.
- ``instanceof`` checks. - ``instanceof`` checks.
@ -69,6 +71,30 @@ new ones like ``\TYPO3\Extbase\...``. This is done for:
- ``catch`` of legacy class names. - ``catch`` of legacy class names.
- Convert old legacy class *definitions* in extensions to namespaces.
- Convert usage of previously converted class definitions. On first run the definition will be
converted, on second run the usage. This is due to the fact, that PHPCS might find the definition
after the usage, so please run twice.
*NOTE* The configured file will be updated after each run, for each converted class, trait and
interface definition. See options.
- Add missing vendor to plugin and module registrations and configurations.
You might want to set this to non fixable and warning if you already provide the vendor inside a
single Variable, together with your extension key, as this is not recognized. So the following
will be recognized:
- ``$_EXTKEY,``
- ``$VENDOR . $_EXTKEY,``
- ``'VENDOR.' . $_EXTKEY,``
While the following will not:
- ``$key = 'Vendor.' . $_EXTKEY;``
Also we check for the following deprecated calls: Also we check for the following deprecated calls:
- Check for ``create`` on ``ObjectManager``, which is "stupid" just all ``create`` calls are marked - Check for ``create`` on ``ObjectManager``, which is "stupid" just all ``create`` calls are marked
@ -152,3 +178,19 @@ Example:
.. code:: bash .. code:: bash
--runtime-set mappingFile /projects/typo3_installation/vendor/composer/autoload_classaliasmap.php --runtime-set mappingFile /projects/typo3_installation/vendor/composer/autoload_classaliasmap.php
``vendor``
Configure your vendor through ``ruleset.xml`` or using ``--runtime-set``. Default is
``YourCompany``.
Example:
.. code:: xml
<config name="vendor" value="YourVendor"/>
Example:
.. code:: bash
--runtime-set vendor YourVendor

View file

@ -22,17 +22,16 @@ namespace Typo3Update\Sniffs\LegacyClassnames;
use PHP_CodeSniffer as PhpCs; use PHP_CodeSniffer as PhpCs;
use PHP_CodeSniffer_File as PhpCsFile; use PHP_CodeSniffer_File as PhpCsFile;
use PHP_CodeSniffer_Sniff as PhpCsSniff;
use Typo3Update\Sniffs\LegacyClassnames\Mapping;
use Typo3Update\Sniffs\OptionsAccessTrait;
/** /**
* Provide common uses for all sniffs, regarding class name checks. * Provide common uses for all sniffs, regarding class name checks.
*/ */
trait ClassnameCheckerTrait abstract class AbstractClassnameChecker implements PhpCsSniff
{ {
/** use OptionsAccessTrait;
* Contains mapping from old -> new class names.
* @var array
*/
private $legacyClassnames = [];
/** /**
* A list of extension names that might contain legacy class names. * A list of extension names that might contain legacy class names.
@ -45,20 +44,22 @@ trait ClassnameCheckerTrait
public $legacyExtensions = ['Extbase', 'Fluid']; public $legacyExtensions = ['Extbase', 'Fluid'];
/** /**
* Initialize, used internally, to not initialize if not needed inside __construct. * @var Mapping
*/ */
private function initialize() protected $legacyMapping;
{
$mappingFile = PhpCs::getConfigData('mappingFile');
if (!$mappingFile) {
$mappingFile = __DIR__ . '/../../../../../LegacyClassnames.php';
}
if ($this->legacyClassnames !== []) {
return;
}
$legacyClassnames = require $mappingFile; /**
$this->legacyClassnames = $legacyClassnames['aliasToClassNameMapping']; * Used by some sniffs to keep original token for replacement.
*
* E.g. when Token itself is a whole inline comment, and we just want to replace the classname within.
*
* @var string
*/
protected $originalTokenContent = '';
public function __construct()
{
$this->legacyMapping = Mapping::getInstance();
} }
/** /**
@ -110,10 +111,9 @@ trait ClassnameCheckerTrait
* @param string $classname * @param string $classname
* @return bool * @return bool
*/ */
private function isLegacyClassname($classname) protected function isLegacyClassname($classname)
{ {
$this->initialize(); return $this->legacyMapping->isLegacyClassname($classname);
return isset($this->legacyClassnames[strtolower($classname)]);
} }
/** /**
@ -145,10 +145,23 @@ trait ClassnameCheckerTrait
* @param string $classname * @param string $classname
* @return string * @return string
*/ */
private function getNewClassname($classname) protected function getNewClassname($classname)
{ {
$this->initialize(); return $this->legacyMapping->getNewClassname($classname);
return $this->legacyClassnames[strtolower($classname)]; }
/**
* Use to add new mappings found during parsing.
* E.g. in MissingNamespaceSniff old class definitions are fixed and a new mapping exists afterwards.
*
* @param string $legacyClassname
* @param string $newClassname
*
* @return void
*/
protected function addLegacyClassname($legacyClassname, $newClassname)
{
$this->legacyMapping->addLegacyClassname($legacyClassname, $newClassname);
} }
/** /**
@ -206,11 +219,16 @@ trait ClassnameCheckerTrait
* @param PhpCsFile $phpcsFile * @param PhpCsFile $phpcsFile
* @param int $classnamePosition * @param int $classnamePosition
* @param string $classname * @param string $classname
* @param bool $forceEmptyPrefix Defines whether '\\' prefix should be checked or always be left out.
*/ */
private function replaceLegacyClassname(PhpCsFile $phpcsFile, $classnamePosition, $classname) protected function replaceLegacyClassname(
{ PhpCsFile $phpcsFile,
$classnamePosition,
$classname,
$forceEmptyPrefix = false
) {
$prefix = '\\'; $prefix = '\\';
if ($phpcsFile->getTokens()[$classnamePosition -1]['code'] === T_NS_SEPARATOR) { if ($forceEmptyPrefix || $phpcsFile->getTokens()[$classnamePosition -1]['code'] === T_NS_SEPARATOR) {
$prefix = ''; $prefix = '';
} }

View file

@ -19,27 +19,22 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_File as PhpCsFile;
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Migrate PHP Doc comments. * Migrate PHP Doc comments.
* *
* E.g. annotations like @param or @return, see $allowedTags. * E.g. annotations like @param or @return, see $allowedTags.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_DocCommentSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_DocCommentSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* The configured tags will be processed. * The configured tags will be processed.
* @var array<string> * @var array<string>
*/ */
public $allowedTags = ['@param', '@return', '@var']; public $allowedTags = ['@param', '@return', '@var'];
/**
* Original token content for reuse accross methods.
* @var string
*/
protected $originalTokenContent = '';
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *
@ -55,13 +50,13 @@ class Typo3Update_Sniffs_LegacyClassnames_DocCommentSniff implements PHP_CodeSni
/** /**
* Processes the tokens that this sniff is interested in. * Processes the tokens that this sniff is interested in.
* *
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param PhpCsFile $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where * @param int $stackPtr The position in the stack where
* the token was found. * the token was found.
* *
* @return void * @return void
*/ */
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) public function process(PhpCsFile $phpcsFile, $stackPtr)
{ {
$tokens = $phpcsFile->getTokens(); $tokens = $phpcsFile->getTokens();
if (!in_array($tokens[$stackPtr]['content'], $this->allowedTags)) { if (!in_array($tokens[$stackPtr]['content'], $this->allowedTags)) {

View file

@ -19,13 +19,14 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_File as PhpCsFile;
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Detect and migrate extend and implement of old legacy classnames. * Detect and migrate extend and implement of old legacy classnames.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_InheritanceSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_InheritanceSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *
@ -38,4 +39,56 @@ class Typo3Update_Sniffs_LegacyClassnames_InheritanceSniff implements PHP_CodeSn
T_IMPLEMENTS, T_IMPLEMENTS,
]; ];
} }
/**
* 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 ($phpcsFile->getTokens()[$stackPtr]['code'] === T_IMPLEMENTS) {
$this->processInterfaces($phpcsFile, $stackPtr);
return;
}
parent::process($phpcsFile, $stackPtr);
}
/**
* Process all interfaces for current class.
*
* @param PhpCsFile $phpcsFile
* @param int $stackPtr
*
* @return void
*/
protected function processInterfaces(PhpCsFile $phpcsFile, $stackPtr)
{
$interfaces = $phpcsFile->findImplementedInterfaceNames($phpcsFile->findPrevious(T_CLASS, $stackPtr));
if ($interfaces === false) {
return;
}
foreach ($interfaces as $interface) {
if (! $this->isLegacyClassname($interface)) {
continue;
}
$position = $phpcsFile->findNext(T_STRING, $stackPtr, null, false, $interface);
if ($position === false) {
continue;
}
$this->addFixableError($phpcsFile, $position, $interface);
}
}
} }

View file

@ -19,19 +19,14 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_File as PhpCsFile;
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Migrate PHP inline comments, e.g. for IDEs. * Migrate PHP inline comments, e.g. for IDEs.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_InlineCommentSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_InlineCommentSniff extends AbstractClassnameChecker
{ {
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. * Returns the token types that this sniff is interested in.
* *
@ -47,19 +42,20 @@ class Typo3Update_Sniffs_LegacyClassnames_InlineCommentSniff implements PHP_Code
/** /**
* Processes the tokens that this sniff is interested in. * Processes the tokens that this sniff is interested in.
* *
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param PhpCsFile $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where * @param int $stackPtr The position in the stack where
* the token was found. * the token was found.
* *
* @return void * @return void
*/ */
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) public function process(PhpCsFile $phpcsFile, $stackPtr)
{ {
$tokens = $phpcsFile->getTokens(); $tokens = $phpcsFile->getTokens();
$this->originalTokenContent = $tokens[$stackPtr]['content']; $this->originalTokenContent = $tokens[$stackPtr]['content'];
$commentParts = preg_split('/\s+/', $this->originalTokenContent); $commentParts = preg_split('/\s+/', $this->originalTokenContent);
if (count($commentParts) !== 5 || $commentParts[1] !== '@var' || ($commentParts[2][0] !== '$' && $commentParts[3][0] !== '$')) { if (count($commentParts) !== 5 || $commentParts[1] !== '@var'
|| ($commentParts[2][0] !== '$' && $commentParts[3][0] !== '$')) {
return; return;
} }

View file

@ -19,13 +19,13 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Detect and migrate instanceof checks of old legacy classnames. * Detect and migrate instanceof checks of old legacy classnames.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_InstanceofSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_InstanceofSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *

View file

@ -19,22 +19,17 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_File as PhpCsFile;
use PHP_CodeSniffer_Tokens as Tokens; use PHP_CodeSniffer_Tokens as Tokens;
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Detect and migrate instantiations of old legacy classnames using "makeInstance". * Detect and migrate instantiations of old legacy classnames using "makeInstance".
*/ */
class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithMakeInstanceSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithMakeInstanceSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait;
/**
* Original token content for reuse accross methods.
* @var string
*/
protected $originalTokenContent = '';
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *
@ -48,13 +43,13 @@ class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithMakeInstanceSniff imp
/** /**
* Processes the tokens that this sniff is interested in. * Processes the tokens that this sniff is interested in.
* *
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param PhpCsFile $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where * @param int $stackPtr The position in the stack where
* the token was found. * the token was found.
* *
* @return void * @return void
*/ */
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) public function process(PhpCsFile $phpcsFile, $stackPtr)
{ {
if (!$this->isFunctionCall($phpcsFile, $stackPtr)) { if (!$this->isFunctionCall($phpcsFile, $stackPtr)) {
return; return;

View file

@ -19,13 +19,13 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Detect and migrate old legacy classnames instantiations using phps "new". * Detect and migrate old legacy classnames instantiations using phps "new".
*/ */
class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithNewSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithNewSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *

View file

@ -19,14 +19,15 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_File as PhpCsFile;
use PHP_CodeSniffer_Tokens as Tokens; use PHP_CodeSniffer_Tokens as Tokens;
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Detect and migrate old legacy classname instantiations using objectmanager create and get. * Detect and migrate old legacy classname instantiations using objectmanager create and get.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithObjectManagerSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithObjectManagerSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait;
/** /**
@ -42,13 +43,13 @@ class Typo3Update_Sniffs_LegacyClassnames_InstantiationWithObjectManagerSniff im
/** /**
* Processes the tokens that this sniff is interested in. * Processes the tokens that this sniff is interested in.
* *
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param PhpCsFile $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where * @param int $stackPtr The position in the stack where
* the token was found. * the token was found.
* *
* @return void * @return void
*/ */
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) public function process(PhpCsFile $phpcsFile, $stackPtr)
{ {
if (!$this->isFunctionCall($phpcsFile, $stackPtr)) { if (!$this->isFunctionCall($phpcsFile, $stackPtr)) {
return; return;

View file

@ -19,22 +19,16 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_Tokens as Tokens; use PHP_CodeSniffer_File as PhpcsFile;
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Detect and migrate instantiations of old legacy classnames using "makeInstance". * Detect and migrate instantiations of old legacy classnames using "makeInstance".
*/ */
class Typo3Update_Sniffs_LegacyClassnames_IsACallSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_IsACallSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait; use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait;
/**
* Original token content for reuse accross methods.
* @var string
*/
protected $originalTokenContent = '';
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *
@ -48,13 +42,13 @@ class Typo3Update_Sniffs_LegacyClassnames_IsACallSniff implements PHP_CodeSniffe
/** /**
* Processes the tokens that this sniff is interested in. * Processes the tokens that this sniff is interested in.
* *
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param PhpCsFile $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where * @param int $stackPtr The position in the stack where
* the token was found. * the token was found.
* *
* @return void * @return void
*/ */
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) public function process(PhpCsFile $phpcsFile, $stackPtr)
{ {
if (!$this->isFunctionCall($phpcsFile, $stackPtr)) { if (!$this->isFunctionCall($phpcsFile, $stackPtr)) {
return; return;

View file

@ -0,0 +1,123 @@
<?php
namespace Typo3Update\Sniffs\LegacyClassnames;
/*
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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 Typo3Update\Sniffs\OptionsAccessTrait;
/**
* Singleton wrapper for mappings.
*
* Will check the configured file for whether a class is legacy and provides further methods.
* Also can update to add new migrated class names.
*/
final class Mapping
{
use OptionsAccessTrait;
// Singleton implementation - Start
static protected $instance = null;
/**
* Get the singleton instance.
*
* @return Mapping
*/
public static function getInstance()
{
if (static::$instance === null) {
static::$instance = new Mapping();
}
return static::$instance;
}
private function __clone()
{
}
private function __wakeup()
{
}
private function __construct()
{
$this->mappings = require $this->getMappingFile();
}
// Singleton implementation - End
/**
* Contains mappings as defined by composer for alias mapping.
* @var array
*/
protected $mappings = [];
/**
* Checks whether a mapping exists for the given $classname,
* indicating it's legacy.
*
* @param string $classname
* @return bool
*/
public function isLegacyClassname($classname)
{
return isset($this->mappings['aliasToClassNameMapping'][strtolower($classname)]);
}
/**
* @param string $classname
* @return string
*/
public function getNewClassname($classname)
{
return $this->mappings['aliasToClassNameMapping'][strtolower($classname)];
}
/**
* Use to add new mappings found during parsing.
* E.g. in MissingNamespaceSniff old class definitions are fixed and a new mapping exists afterwards.
*
* @param string $legacyClassname
* @param string $newClassname
*
* @return void
*/
public function addLegacyClassname($legacyClassname, $newClassname)
{
$key = strtolower($legacyClassname);
$this->mappings['aliasToClassNameMapping'][$key] = $newClassname;
$this->mappings['classNameToAliasMapping'][$newClassname] = [$key => $key];
}
/**
* Used to persist new mappings.
*/
public function __destruct()
{
// For some reasons desctruct is called multiple times, while construct
// is called once. Until we know the issue and fix it, this is our
// workaround to not break the file and do stuff in an unkown instance.
if ($this !== static::$instance) {
return;
}
file_put_contents(
$this->getMappingFile(),
'<?php' . PHP_EOL . 'return ' . var_export($this->mappings, true) . ';'
);
}
}

View file

@ -0,0 +1,164 @@
<?php
/*
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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<int>
*/
public function register()
{
return [
T_CLASS,
T_INTERFACE,
T_TRAIT,
];
}
/**
* 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
* @param bool $forceEmptyPrefix Defines whether '\\' prefix should be checked or always be left out.
*/
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;
if ($tokens[1]['code'] !== T_WHITESPACE) {
$suffix .= $lineEndings;
}
$phpcsFile->fixer->replaceToken(
$this->getNamespacePosition($phpcsFile),
'<?php' . $lineEndings . $this->getNamespaceDefinition($classname) . $suffix
);
$this->addLegacyClassname(
$classname,
$this->getNamespace($classname) . '\\' . $this->getNewClassname($classname)
);
}
/**
* @param PhpCsFile $phpcsFile
* @return int|false
*/
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)
{
return 'namespace ' . $this->getNamespace($classname) . ';';
}
/**
* Returns namespace, based on legacy class name.
*
* E.g. Tx_ExtName_FolderName_FileName -> VENDOR\ExtName\FolderName
*
* @param string $classname
* @return string
*/
protected function getNamespace($classname)
{
$vendor = trim($this->getVendor(), '\\/');
$classnameParts = explode('_', $classname);
unset($classnameParts[0]); // Remove Tx_
unset($classnameParts[count($classnameParts)]); // Remove class name itself.
return $vendor . '\\' . implode('\\', $classnameParts);
}
}

View file

@ -0,0 +1,114 @@
<?php
/*
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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;
/**
* Detect whether vendor is missing for plugins and modules registrations and configurations.
*/
class Typo3Update_Sniffs_LegacyClassnames_MissingVendorForPluginsAndModulesSniff implements PhpCsSniff
{
use \Typo3Update\Sniffs\ExtendedPhpCsSupportTrait;
use \Typo3Update\Sniffs\OptionsAccessTrait;
/**
* Returns the token types that this sniff is interested in.
*
* @return array<int>
*/
public function register()
{
return Tokens::$functionNameTokens;
}
/**
* 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)
{
$functionsToHandle = ['registerPlugin', 'configurePlugin', 'registerModule'];
if (!$this->isFunctionCall($phpcsFile, $stackPtr)) {
return;
}
$tokens = $phpcsFile->getTokens();
if (!in_array($tokens[$stackPtr]['content'], $functionsToHandle)) {
return;
}
$firstArgument = $phpcsFile->findNext([T_WHITESPACE, T_OPEN_PARENTHESIS], $stackPtr + 1, null, true);
if (! $this->isVendorMissing($phpcsFile, $firstArgument)) {
return;
}
$fix = $phpcsFile->addFixableError(
'No vendor is given, that will break TYPO3 handling for namespaced classes.'
. ' Add vendor before Extensionkey like: "%s." . $_EXTKEY',
$firstArgument,
'missingVendor',
[$this->getVendor()]
);
if ($fix === true) {
$phpcsFile->fixer->replaceToken(
$firstArgument,
"'{$this->getVendor()}.' . {$tokens[$firstArgument]['content']}"
);
}
}
/**
* Checks whether vendor is missing in given argument.
*
* @param PhpCsFile $phpcsFile
* @param int|bool $argumentStart
*
* @return bool
*/
protected function isVendorMissing(PhpCsFile $phpcsFile, $argumentStart)
{
if ($argumentStart === false) {
return false;
}
$argumentEnd = $phpcsFile->findNext(T_COMMA, $argumentStart);
if ($argumentEnd === false) {
return false;
}
$stringConcats = array_filter(
array_slice($phpcsFile->getTokens(), $argumentStart, $argumentEnd - $argumentStart),
function (array $token) {
return $token['code'] === 'PHPCS_T_STRING_CONCAT';
}
);
return count($stringConcats) === 0;
}
}

View file

@ -19,13 +19,13 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Detect and migrate static calls to old legacy classnames. * Detect and migrate static calls to old legacy classnames.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_StaticCallSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_StaticCallSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* Define whether the T_STRING default behaviour should be checked before * Define whether the T_STRING default behaviour should be checked before
* or after the $stackPtr. * or after the $stackPtr.

View file

@ -19,15 +19,13 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_Tokens as Tokens; use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Migrate Typehints in catch statements. * Migrate Typehints in catch statements.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_TypehintCatchExceptionSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_TypehintCatchExceptionSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *

View file

@ -19,15 +19,14 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_Tokens as Tokens; use PHP_CodeSniffer_File as PhpCsFile;
use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Migrate Typehints in function / method definitions. * Migrate Typehints in function / method definitions.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_TypehintSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_TypehintSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *
@ -41,13 +40,13 @@ class Typo3Update_Sniffs_LegacyClassnames_TypehintSniff implements PHP_CodeSniff
/** /**
* Processes the tokens that this sniff is interested in. * Processes the tokens that this sniff is interested in.
* *
* @param PHP_CodeSniffer_File $phpcsFile The file where the token was found. * @param PhpCsFile $phpcsFile The file where the token was found.
* @param int $stackPtr The position in the stack where * @param int $stackPtr The position in the stack where
* the token was found. * the token was found.
* *
* @return void * @return void
*/ */
public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr) public function process(PhpCsFile $phpcsFile, $stackPtr)
{ {
$params = $phpcsFile->getMethodParameters($stackPtr); $params = $phpcsFile->getMethodParameters($stackPtr);
foreach ($params as $parameter) { foreach ($params as $parameter) {

View file

@ -19,15 +19,13 @@
* 02110-1301, USA. * 02110-1301, USA.
*/ */
use PHP_CodeSniffer_Tokens as Tokens; use Typo3Update\Sniffs\LegacyClassnames\AbstractClassnameChecker;
/** /**
* Migrate old legacy class names in use statements. * Migrate old legacy class names in use statements.
*/ */
class Typo3Update_Sniffs_LegacyClassnames_UseSniff implements PHP_CodeSniffer_Sniff class Typo3Update_Sniffs_LegacyClassnames_UseSniff extends AbstractClassnameChecker
{ {
use \Typo3Update\Sniffs\LegacyClassnames\ClassnameCheckerTrait;
/** /**
* Returns the token types that this sniff is interested in. * Returns the token types that this sniff is interested in.
* *

View file

@ -0,0 +1,57 @@
<?php
namespace Typo3Update\Sniffs;
/*
* Copyright (C) 2017 Daniel Siepmann <coding@daniel-siepmann.de>
*
* 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 as PhpCs;
/**
* Wrapper to retrieve options from PhpCs with defaults.
*/
trait OptionsAccessTrait
{
/**
* Returns the configured vendor, e.g. to generate new namespaces.
*
* @return string
*/
public function getVendor()
{
$vendor = PhpCs::getConfigData('vendor');
if (!$vendor) {
$vendor = 'YourCompany';
}
return trim($vendor, '\\/');
}
/**
* Returns the configured file path containing the mappings for classes, interfaced and traits.
*
* @return string
*/
public function getMappingFile()
{
$mappingFile = PhpCs::getConfigData('mappingFile');
if (!$mappingFile) {
$mappingFile = __DIR__ . '/../../../../LegacyClassnames.php';
}
return $mappingFile;
}
}

View file

@ -12,4 +12,8 @@
<property name="allowedTags" type="array" value="@param,@return,@var,@see,@throws"/> <property name="allowedTags" type="array" value="@param,@return,@var,@see,@throws"/>
</properties> </properties>
</rule> </rule>
<rule ref="Typo3Update.LegacyClassnames.MissingNamespace.legacyClassname">
<message>Legacy class definitions are not allowed; found "%s". Wrap your class inside a namespace.</message>
</rule>
</ruleset> </ruleset>