Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 4 additions & 7 deletions Classes/Command/RepositoryCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -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".');
Expand Down
12 changes: 6 additions & 6 deletions Classes/Command/TypeConverterCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -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".');
Expand Down
12 changes: 6 additions & 6 deletions Classes/Command/UpgradeWizardCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -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".');
Expand Down
12 changes: 6 additions & 6 deletions Classes/Command/ValidatorCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -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.');
Expand Down
112 changes: 112 additions & 0 deletions Classes/Validator/PhpClassNameValidator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
<?php

declare(strict_types=1);

/*
* This file is part of the package friendsoftypo3/kickstarter.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/

namespace FriendsOfTYPO3\Kickstarter\Validator;

final readonly class PhpClassNameValidator
{
/**
* List of reserved keywords in PHP that cannot be used as class names (case-insensitive).
* This list is a common compilation of hard-reserved words.
* Some of these are context-sensitive keywords in newer PHP versions but are often
* treated as reserved in the context of class/interface/trait names for compatibility
* or future-proofing.
* Note: 'void', 'iterable', 'object', etc., are not reserved as class names
* in older PHP versions but might be in stricter contexts.
*
* @var array<string>
*/
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);
}
}