diff --git a/Classes/Command/RepositoryCommand.php b/Classes/Command/RepositoryCommand.php index f7cfde2..d8d5268 100644 --- a/Classes/Command/RepositoryCommand.php +++ b/Classes/Command/RepositoryCommand.php @@ -19,6 +19,7 @@ use FriendsOfTYPO3\Kickstarter\Traits\CreatorInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\ExtensionInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\TryToCorrectClassNameTrait; +use FriendsOfTYPO3\Kickstarter\Validator\PhpClassNameValidator; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -34,6 +35,7 @@ class RepositoryCommand extends Command public function __construct( private readonly RepositoryCreatorService $repositoryCreatorService, private readonly QuestionCollection $questionCollection, + private readonly PhpClassNameValidator $classNameValidator, ) { parent::__construct(); } @@ -96,13 +98,8 @@ private function askForRepositoryClassName(SymfonyStyle $io): string if ($repositoryClassName === '') { $io->error('Class name can not be empty.'); $validRepositoryClassName = false; - } elseif (preg_match('/^\d/', $repositoryClassName)) { - $io->error('Class name should not start with a number.'); - $defaultRepositoryClassName = $this->tryToCorrectClassName($repositoryClassName, 'Repository'); - $validRepositoryClassName = false; - } elseif (preg_match('/[^a-zA-Z0-9]/', $repositoryClassName)) { - $io->error('Class name contains invalid chars. Please provide just letters and numbers.'); - $defaultRepositoryClassName = $this->tryToCorrectClassName($repositoryClassName, 'Repository'); + } elseif (!$this->classNameValidator->validate($repositoryClassName)) { + $io->error('Class name is not a valid php class name.'); $validRepositoryClassName = false; } elseif (preg_match('/^[A-Z][a-zA-Z0-9]+$/', $repositoryClassName) === 0) { $io->error('Action must be written in UpperCamelCase like "CarRepository".'); diff --git a/Classes/Command/TypeConverterCommand.php b/Classes/Command/TypeConverterCommand.php index 5677bf8..777b0ac 100644 --- a/Classes/Command/TypeConverterCommand.php +++ b/Classes/Command/TypeConverterCommand.php @@ -19,6 +19,7 @@ use FriendsOfTYPO3\Kickstarter\Traits\CreatorInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\ExtensionInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\TryToCorrectClassNameTrait; +use FriendsOfTYPO3\Kickstarter\Validator\PhpClassNameValidator; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -34,6 +35,7 @@ class TypeConverterCommand extends Command public function __construct( private readonly TypeConverterCreatorService $typeConverterCreatorService, private readonly QuestionCollection $questionCollection, + private readonly PhpClassNameValidator $classNameValidator, ) { parent::__construct(); } @@ -96,13 +98,11 @@ private function askForTypeConverterClassName(SymfonyStyle $io): string $defaultTypeConverterClassName, ); - if (preg_match('/^\d/', $typeConverterClassName)) { - $io->error('Class name should not start with a number.'); - $defaultTypeConverterClassName = $this->tryToCorrectClassName($typeConverterClassName, 'Converter'); + if ($typeConverterClassName === '') { + $io->error('Class name can not be empty.'); $validTypeConverterClassName = false; - } elseif (preg_match('/[^a-zA-Z0-9]/', $typeConverterClassName)) { - $io->error('Class name contains invalid chars. Please provide just letters and numbers.'); - $defaultTypeConverterClassName = $this->tryToCorrectClassName($typeConverterClassName, 'Converter'); + } elseif (!$this->classNameValidator->validate($typeConverterClassName)) { + $io->error('Class name is not a valid php class name.'); $validTypeConverterClassName = false; } elseif (preg_match('/^[A-Z][a-zA-Z0-9]+$/', $typeConverterClassName) === 0) { $io->error('Class name must be written in UpperCamelCase like "FileUploadConverter".'); diff --git a/Classes/Command/UpgradeWizardCommand.php b/Classes/Command/UpgradeWizardCommand.php index d3c15b1..ec9532c 100644 --- a/Classes/Command/UpgradeWizardCommand.php +++ b/Classes/Command/UpgradeWizardCommand.php @@ -19,6 +19,7 @@ use FriendsOfTYPO3\Kickstarter\Traits\CreatorInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\ExtensionInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\TryToCorrectClassNameTrait; +use FriendsOfTYPO3\Kickstarter\Validator\PhpClassNameValidator; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -34,6 +35,7 @@ class UpgradeWizardCommand extends Command public function __construct( private readonly UpgradeWizardCreatorService $upgradeWizardCreatorService, private readonly QuestionCollection $questionCollection, + private readonly PhpClassNameValidator $classNameValidator, ) { parent::__construct(); } @@ -93,13 +95,11 @@ private function askForUpgradeWizardClassName(SymfonyStyle $io): string $defaultUpgradeWizardClassName, ); - if (preg_match('/^\d/', $upgradeWizardClassName)) { - $io->error('Class name should not start with a number.'); - $defaultUpgradeWizardClassName = $this->tryToCorrectClassName($upgradeWizardClassName, 'Upgrade'); + if ($upgradeWizardClassName === '') { + $io->error('Class name can not be empty.'); $validUpgradeWizardClassName = false; - } elseif (preg_match('/[^a-zA-Z0-9]/', $upgradeWizardClassName)) { - $io->error('Class name contains invalid chars. Please provide just letters and numbers.'); - $defaultUpgradeWizardClassName = $this->tryToCorrectClassName($upgradeWizardClassName, 'Upgrade'); + } elseif (!$this->classNameValidator->validate($upgradeWizardClassName)) { + $io->error('Class name is not a valid php class name.'); $validUpgradeWizardClassName = false; } elseif (preg_match('/^[A-Z][a-zA-Z0-9]+$/', $upgradeWizardClassName) === 0) { $io->error('Action must be written in UpperCamelCase like "CorrectPluginUpgrade".'); diff --git a/Classes/Command/ValidatorCommand.php b/Classes/Command/ValidatorCommand.php index 0e5532b..7dc34f2 100644 --- a/Classes/Command/ValidatorCommand.php +++ b/Classes/Command/ValidatorCommand.php @@ -22,6 +22,7 @@ use FriendsOfTYPO3\Kickstarter\Traits\CreatorInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\ExtensionInformationTrait; use FriendsOfTYPO3\Kickstarter\Traits\TryToCorrectClassNameTrait; +use FriendsOfTYPO3\Kickstarter\Validator\PhpClassNameValidator; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -38,6 +39,7 @@ class ValidatorCommand extends Command public function __construct( private readonly ValidatorCreatorService $validatorCreatorService, private readonly QuestionCollection $questionCollection, + private readonly PhpClassNameValidator $classNameValidator, ) { parent::__construct(); } @@ -122,13 +124,11 @@ private function askForValidatorName(SymfonyStyle $io): string $defaultName, ); - if (preg_match('/^\d/', $name)) { - $io->error('Validator name should not start with a number.'); - $defaultName = $this->tryToCorrectClassName($name, 'Validator'); + if ($name === '') { + $io->error('Class name can not be empty.'); $validValidatorName = false; - } elseif (preg_match('/[^a-zA-Z0-9]/', $name)) { - $io->error('Validator name contains invalid chars. Please provide just letters and numbers.'); - $defaultName = $this->tryToCorrectClassName($name, 'Validator'); + } elseif (!$this->classNameValidator->validate($name)) { + $io->error('Class name is not a valid php class name.'); $validValidatorName = false; } elseif (preg_match('/^[a-z0-9]+$/', $name)) { $io->error('Validator must be written in UpperCamelCase like BlogExampleValidator.'); diff --git a/Classes/Validator/PhpClassNameValidator.php b/Classes/Validator/PhpClassNameValidator.php new file mode 100644 index 0000000..413d4a5 --- /dev/null +++ b/Classes/Validator/PhpClassNameValidator.php @@ -0,0 +1,112 @@ + + */ + private const RESERVED_KEYWORDS = [ + '__halt_compiler', + 'abstract', + 'and', + 'array', + 'as', + 'break', + 'callable', + 'case', + 'catch', + 'class', + 'clone', + 'const', + 'continue', + 'declare', + 'default', + 'die', + 'do', + 'echo', + 'else', + 'elseif', + 'empty', + 'enddeclare', + 'endfor', + 'endforeach', + 'endif', + 'endswitch', + 'endwhile', + 'enum', // Added for PHP 8.1+ + 'exit', + 'extends', + 'final', + 'finally', + 'fn', // Added for PHP 7.4+ + 'for', + 'foreach', + 'function', + 'global', + 'goto', + 'if', + 'implements', + 'include', + 'include_once', + 'instanceof', + 'insteadof', + 'interface', + 'isset', + 'list', + 'match', // Added for PHP 8.0+ + 'namespace', + 'new', + 'or', + 'parent', + 'print', + 'private', + 'protected', + 'public', + 'readonly', // Added for PHP 8.1+ + 'require', + 'require_once', + 'return', + 'self', + 'static', + 'switch', + 'throw', + 'trait', + 'try', + 'unset', + 'use', + 'var', + 'while', + 'xor', + 'yield', + ]; + + public function validate(string $className): bool + { + // regex from https://www.php.net/manual/en/language.oop5.basic.php + $isValidFormat = (bool)preg_match('/^[a-zA-Z_\x80-\xff][a-zA-Z0-9_\x80-\xff]*$/', $className); + if (!$isValidFormat) { + return false; + } + + return !in_array(strtolower($className), self::RESERVED_KEYWORDS, true); + } +}