Skip to content

Commit 00cf423

Browse files
committed
Separate className validator and namespace validator.
1 parent d712b11 commit 00cf423

File tree

5 files changed

+63
-6
lines changed

5 files changed

+63
-6
lines changed

src/Generator.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ public function createClassNameDetails(string $name, string $namespacePrefix, st
141141
$className = rtrim($fullNamespacePrefix, '\\').'\\'.Str::asClassName($name, $suffix);
142142
}
143143

144+
Validator::validateNamespace($className);
144145
Validator::validateClassName($className, $validationErrorMessage);
145146

146147
// if this is a custom class, we may be completely different than the namespace prefix

src/Str.php

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,14 +210,12 @@ public static function isValidPhpClassName(string $className): bool
210210
'list', 'namespace', 'new', 'or', 'print', 'private',
211211
'protected', 'public', 'require', 'require_once', 'return',
212212
'static', 'switch', 'throw', 'trait', 'try', 'unset',
213-
'use', 'var', 'while', 'xor', '__CLASS__', '__DIR__', '__FILE__',
214-
'__FUNCTION__', '__LINE__', '__METHOD__', '__NAMESPACE__', '__TRAIT__',
213+
'use', 'var', 'while', 'xor',
215214
'int', 'float', 'bool', 'string', 'true', 'false', 'null', 'void',
216215
'iterable', 'object',
217216
];
218217

219-
return !\in_array(strtolower($className), $reservedKeywords, true) &&
220-
preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $className);
218+
return !\in_array(strtolower(self::getShortClassName($className)), $reservedKeywords, true);
221219
}
222220

223221
public static function areClassesAlphabetical(string $class1, string $class2)
@@ -233,4 +231,22 @@ public static function asHumanWords(string $variableName): string
233231
{
234232
return implode(' ', preg_split('/(?=[A-Z])/', $variableName));
235233
}
234+
235+
public static function isValidPhpNamespace(string $className): bool
236+
{
237+
$predefinedConstants = ['__CLASS__', '__DIR__', '__FILE__', '__FUNCTION__',
238+
'__LINE__', '__METHOD__', '__NAMESPACE__', '__TRAIT__',
239+
];
240+
241+
$namespace = self::getNamespace($className);
242+
$pieces = explode('\\', $namespace);
243+
244+
foreach ($pieces as $piece) {
245+
if (\in_array(strtoupper($piece), $predefinedConstants, true)) {
246+
return false;
247+
}
248+
}
249+
250+
return true;
251+
}
236252
}

src/Validator.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,17 @@ public static function validateClassName(string $className, string $errorMessage
2929
$pieces = explode('\\', ltrim($className, '\\'));
3030

3131
foreach ($pieces as $piece) {
32-
if (!Str::isValidPhpClassName($piece)) {
32+
if (!preg_match('/^[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*$/', $piece)) {
3333
$errorMessage = $errorMessage ?: sprintf('"%s" is not valid as a PHP class name (it must start with a letter or underscore, followed by any number of letters, numbers, or underscores)', $className);
3434

3535
throw new RuntimeCommandException($errorMessage);
3636
}
3737
}
3838

39+
if (!Str::isValidPhpClassName($className)) {
40+
throw new RuntimeCommandException(sprintf('"%s" is a reserved keyword and thus cannot be used as class name in PHP.', $className));
41+
}
42+
3943
// return original class name
4044
return $className;
4145
}
@@ -207,4 +211,13 @@ public static function classIsUserInterface($userClassName): string
207211

208212
return $userClassName;
209213
}
214+
215+
public static function validateNamespace(string $className): string
216+
{
217+
if (!Str::isValidPhpNamespace($className)) {
218+
throw new RuntimeCommandException(sprintf('"%s" is a predefined constant and thus cannot be used as part of a namespace.', $className));
219+
}
220+
221+
return $className;
222+
}
210223
}

tests/StrTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ public function testAsTwigVariable($value, $expectedResult)
3737
$this->assertSame($expectedResult, Str::asTwigVariable($value));
3838
}
3939

40+
/** @dataProvider provideIsValidPhpClassName */
41+
public function testIsValidPhpClassName($expected, $className)
42+
{
43+
$this->assertSame($expected, Str::isValidPhpClassName($className));
44+
}
45+
4046
public function provideHasSuffix()
4147
{
4248
yield ['', '', true];
@@ -175,4 +181,12 @@ public function getShortClassNameCaseTests()
175181
yield ['App\\Entity\\Foo', 'Foo'];
176182
yield ['Foo', 'Foo'];
177183
}
184+
185+
public function provideIsValidPhpClassName()
186+
{
187+
yield [false, 'App\\Entity\\Class'];
188+
yield [true, 'App\\Class\\Service'];
189+
yield [false, 'App\\Entity\\Object'];
190+
yield [true, 'App\\Foo\\Bar'];
191+
}
178192
}

tests/ValidatorTest.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,20 @@ public function testValidateClassName()
5959
public function testInvalidClassName()
6060
{
6161
$this->expectException(RuntimeCommandException::class);
62-
$this->expectExceptionMessage('"class" is not valid as a PHP class name (it must start with a letter or underscore, followed by any number of letters, numbers, or underscores)');
62+
$this->expectExceptionMessage('"class" is a reserved keyword and thus cannot be used as class name in PHP.');
6363
Validator::validateClassName('class');
6464
}
65+
66+
public function testInvalidNamespace()
67+
{
68+
$this->expectException(RuntimeCommandException::class);
69+
$this->expectExceptionMessage('"App\__CLASS__\FooBar" is a predefined constant and thus cannot be used as part of a namespace.');
70+
Validator::validateNamespace('App\__CLASS__\FooBar');
71+
}
72+
73+
public function testValidNamespaceInvalidClassName()
74+
{
75+
$this->assertSame('\App\object\Foo', Validator::validateClassName('\App\object\Foo'));
76+
$this->assertSame('\App\class\Foo', Validator::validateClassName('\App\class\Foo'));
77+
}
6578
}

0 commit comments

Comments
 (0)