diff --git a/Readme.rst b/Readme.rst
index 86f6a6e..1c13268 100644
--- a/Readme.rst
+++ b/Readme.rst
@@ -69,6 +69,8 @@ new ones like ``\TYPO3\Extbase\...``. This is done for:
- ``catch`` of legacy class names.
+- Convert old legacy class definitions in extensions to namespace ones.
+
Also we check for the following deprecated calls:
- Check for ``create`` on ``ObjectManager``, which is "stupid" just all ``create`` calls are marked
@@ -136,3 +138,19 @@ Typo3Update.LegacyClassnames.DocComment: ``allowedTags``
+
+``vendor``
+ Configure your vendor through ``ruleset.xml`` or using ``--runtime-set``. Default is
+ ``YourCompany``.
+
+ Example:
+
+.. code:: xml
+
+
+
+Example:
+
+.. code:: bash
+
+ --runtime-set vendor YourVendor
diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php
similarity index 90%
rename from src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php
rename to src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php
index d6a2fe2..305213e 100644
--- a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/ClassnameCheckerTrait.php
+++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/AbstractClassnameChecker.php
@@ -20,12 +20,14 @@ namespace Typo3Update\Sniffs\LegacyClassnames;
* 02110-1301, USA.
*/
+use PHP_CodeSniffer as PhpCs;
use PHP_CodeSniffer_File as PhpcsFile;
+use PHP_CodeSniffer_Sniff as PhpCsSniff;
/**
* Provide common uses for all sniffs, regarding class name checks.
*/
-trait ClassnameCheckerTrait
+abstract class AbstractClassnameChecker implements PhpCsSniff
{
/**
* Contains mapping from old -> new class names.
@@ -43,6 +45,20 @@ trait ClassnameCheckerTrait
*/
public $legacyExtensions = ['Extbase', 'Fluid'];
+ /**
+ * Returns the configured vendor, e.g. to generate new namespaces.
+ *
+ * @return string
+ */
+ protected function getVendor()
+ {
+ $vendor = PhpCs::getConfigData('vendor');
+ if (!$vendor) {
+ $vendor = 'YourCompany';
+ }
+ return trim($vendor, '\\/');
+ }
+
/**
* @param string $mappingFile File containing php array for mapping.
*/
@@ -103,7 +119,7 @@ trait ClassnameCheckerTrait
* @param string $classname
* @return bool
*/
- private function isLegacyClassname($classname)
+ protected function isLegacyClassname($classname)
{
$this->initialize();
return isset($this->legacyClassnames[strtolower($classname)]);
@@ -138,7 +154,7 @@ trait ClassnameCheckerTrait
* @param string $classname
* @return string
*/
- private function getNewClassname($classname)
+ protected function getNewClassname($classname)
{
$this->initialize();
return $this->legacyClassnames[strtolower($classname)];
@@ -200,10 +216,10 @@ trait ClassnameCheckerTrait
* @param int $classnamePosition
* @param string $classname
*/
- private function replaceLegacyClassname(PhpcsFile $phpcsFile, $classnamePosition, $classname)
+ protected function replaceLegacyClassname(PhpcsFile $phpcsFile, $classnamePosition, $classname, $forceEmptyPrefix = false)
{
$prefix = '\\';
- if ($phpcsFile->getTokens()[$classnamePosition -1]['code'] === T_NS_SEPARATOR) {
+ if ($forceEmptyPrefix || $phpcsFile->getTokens()[$classnamePosition -1]['code'] === T_NS_SEPARATOR) {
$prefix = '';
}
diff --git a/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php
new file mode 100644
index 0000000..2d74d70
--- /dev/null
+++ b/src/Standards/Typo3Update/Sniffs/LegacyClassnames/MissingNamespaceSniff.php
@@ -0,0 +1,157 @@
+
+ *
+ * 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
+ */
+ public function register()
+ {
+ return [T_CLASS];
+ }
+
+ /**
+ * 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
+ */
+ 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;
+
+ // TODO, does not work.
+ if ($tokens[1] === "\n") {
+ $suffix .= $lineEndings;
+ }
+
+ $phpcsFile->fixer->replaceToken(
+ $this->getNamespacePosition($phpcsFile),
+ 'getNamespaceDefinition($classname) . $suffix
+ );
+ }
+
+ /**
+ * @param PhpCsFile $phpcsFile
+ * @return int
+ */
+ 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)
+ {
+ $vendor = trim($this->getVendor(), '\\/');
+
+ return 'namespace '
+ . $vendor
+ . '\\'
+ . $this->getNamespace($classname)
+ . ';'
+ ;
+ }
+
+ /**
+ * Returns namespace, without vendor, based on legacy class name.
+ *
+ * E.g. Tx_ExtName_FolderName_FileName -> ExtName\FolderName
+ *
+ * @param string $classname
+ * @return string
+ */
+ protected function getNamespace($classname)
+ {
+ $classnameParts = explode('_', $classname);
+
+ unset($classnameParts[0]); // Remove Tx_
+ unset($classnameParts[count($classnameParts)]); // Remove class name itself.
+
+ return implode('\\', $classnameParts);
+ }
+}
diff --git a/src/Standards/Typo3Update/ruleset.xml b/src/Standards/Typo3Update/ruleset.xml
index c8e6a69..260cadb 100644
--- a/src/Standards/Typo3Update/ruleset.xml
+++ b/src/Standards/Typo3Update/ruleset.xml
@@ -12,4 +12,8 @@
+
+
+ Legacy class definitions are not allowed; found "%s". Wrap your class inside a namespace.
+