Skip to content

Commit 88a1466

Browse files
committed
tests
1 parent 75280bf commit 88a1466

File tree

135 files changed

+1460
-39
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

135 files changed

+1460
-39
lines changed

LICENSE.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
The MIT License
2+
3+
Copyright (c) 2021 Sukhachev Anton
4+
5+
Permission is hereby granted, free of charge, to any person obtaining
6+
a copy of this software and associated documentation files (the
7+
"Software"), to deal in the Software without restriction, including
8+
without limitation the rights to use, copy, modify, merge, publish,
9+
distribute, sublicense, and/or sell copies of the Software, and to
10+
permit persons to whom the Software is furnished to do so, subject to
11+
the following conditions:
12+
13+
The above copyright notice and this permission notice shall be
14+
included in all copies or substantial portions of the Software.
15+
16+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

bin/generate.php

+11-1
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,19 @@
1212

1313
$sourceDir = $argv[1];
1414
if (empty($sourceDir) || !is_dir($sourceDir)) {
15+
echo "Source directory doesn't exists\n";
1516
exit(1);
1617
}
1718

1819
$outputDir = $argv[2];
1920
if (empty($outputDir) || !is_dir($outputDir)) {
21+
echo "Output directory doesn't exists\n";
2022
exit(1);
2123
}
2224

2325
$psr4Prefix = $argv[3];
2426
if (empty($psr4Prefix)) {
27+
echo "Invalid prefix\n";
2528
exit(1);
2629
}
2730

@@ -36,7 +39,14 @@
3639

3740
$compiler = new Compiler($classFinder);
3841

39-
$result = $compiler->compile($sourceDir);
42+
try {
43+
$result = $compiler->compile($sourceDir);
44+
} catch (\Exception $exception) {
45+
echo $exception->getMessage() . PHP_EOL;
46+
echo $exception->getTraceAsString() . PHP_EOL;
47+
exit(1);
48+
}
49+
4050
foreach ($result->getConcreteClasses() as $concreteClass) {
4151
$concreteFilePath = $outputDir . DIRECTORY_SEPARATOR . ltrim($classFinder->getRelativeFilePathByClassFqn($concreteClass->fqn), DIRECTORY_SEPARATOR);
4252
$filesystem->ensureDirectoryExists(dirname($concreteFilePath));

bin/test.php

+15-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,13 @@
3737

3838
$compiler = new Compiler($classFinder);
3939

40-
$result = $compiler->compile($inputDirectory);
40+
try {
41+
$result = $compiler->compile($inputDirectory);
42+
} catch (\Exception $exception) {
43+
echo $exception->getMessage() . PHP_EOL;
44+
echo $exception->getTraceAsString() . PHP_EOL;
45+
exit(1);
46+
}
4147

4248
if ($outputFiles->count() !== count($result->getConcreteClasses())) {
4349
$success = false;
@@ -47,7 +53,14 @@
4753
foreach ($result->getConcreteClasses() as $concreteClass) {
4854
$concreteFilePath = $outputDirectory . DIRECTORY_SEPARATOR . ltrim($classFinder->getRelativeFilePathByClassFqn($concreteClass->fqn), DIRECTORY_SEPARATOR);
4955
$concreteClassContent = file_get_contents($concreteFilePath);
50-
$concreteClassAst = Parser::parse($concreteClassContent);
56+
try {
57+
$concreteClassAst = Parser::parse($concreteClassContent);
58+
} catch (\Exception $exception) {
59+
echo sprintf('Parse file "%s" error: "%s"', $concreteFilePath, $exception->getMessage()) . PHP_EOL;
60+
echo $exception->getTraceAsString() . PHP_EOL;
61+
exit(1);
62+
}
63+
5164
if ($printer->printFile($concreteClassAst) !== $printer->printFile($concreteClass->ast)) {
5265
$success = false;
5366
}

composer.json

+10
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,16 @@
22
"name": "php-generics/library",
33
"type": "composer-plugin",
44
"license": "MIT",
5+
"description": "PHP Generics library",
6+
"keywords": [
7+
"generics"
8+
],
9+
"authors": [
10+
{
11+
"name": "Sukhachev Anton",
12+
"email": "[email protected]"
13+
}
14+
],
515
"autoload": {
616
"psr-4": {
717
"Mrsuh\\PhpGenerics\\": "src/"

composer.lock

+3-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Command/DumpCommand.php

+11-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ protected function configure()
2121
->setDescription('Dumps the concrete class files from generics classes');
2222
}
2323

24-
protected function execute(InputInterface $input, OutputInterface $output)
24+
protected function execute(InputInterface $input, OutputInterface $output): int
2525
{
2626
$timeStart = microtime(true);
2727
$this->getIO()->write('<info>Generating concrete classes</info>');
@@ -67,7 +67,14 @@ protected function execute(InputInterface $input, OutputInterface $output)
6767

6868
$filesystem->emptyDirectory($cacheDir);
6969

70-
$result = $compiler->compile($sourceDir);
70+
try {
71+
$result = $compiler->compile($sourceDir);
72+
} catch (\Exception $exception) {
73+
$this->getIO()->writeError($exception->getMessage());
74+
$this->getIO()->writeError($exception->getTraceAsString());
75+
76+
return 1;
77+
}
7178

7279
foreach ($result->getConcreteClasses() as $concreteClass) {
7380
$concreteFilePath = $cacheDir . DIRECTORY_SEPARATOR . ltrim($classFinder->getRelativeFilePathByClassFqn($concreteClass->fqn), DIRECTORY_SEPARATOR);
@@ -88,6 +95,8 @@ protected function execute(InputInterface $input, OutputInterface $output)
8895
$timeFin - $timeStart,
8996
memory_get_usage(true) / 1024 / 1024)
9097
);
98+
99+
return 0;
91100
}
92101

93102
private function getClassLoader(array $autoloads, AutoloadGenerator $autoloadGenerator, Filesystem $filesystem, string $basePath, string $vendorPath): ClassLoader

src/Compiler/Compiler.php

+7-3
Original file line numberDiff line numberDiff line change
@@ -41,13 +41,17 @@ public function compile(string $directory): Result
4141
throw new \RuntimeException(sprintf('File "%s" has empty content', $sourceFilePath));
4242
}
4343

44-
$ast = Parser::resolveNames(Parser::parse($content));
44+
try {
45+
$ast = Parser::resolveNames(Parser::parse($content));
46+
} catch (\Exception $exception) {
47+
throw new \RuntimeException(sprintf('Can\'t parse file "%s"', $sourceFilePath), $exception->getCode(), $exception);
48+
}
49+
4550
if (!Parser::hasGenericClassUsages($ast)) {
4651
continue;
4752
}
4853

49-
$usageGenericClass = new GenericClass($this->classFinder, $concreteClassCache, $genericClassCache, $ast);
50-
54+
$usageGenericClass = new GenericClass($this->classFinder, $concreteClassCache, $genericClassCache, $ast);
5155
$usageConcreteClass = $usageGenericClass->generateConcreteClass([], $result);
5256
$result->addConcreteClass($usageConcreteClass);
5357
}

src/Compiler/GenericClass.php

+13-11
Original file line numberDiff line numberDiff line change
@@ -152,17 +152,15 @@ public function generateConcreteClass(array $arguments, Result $result): Concret
152152
}
153153
}
154154

155-
if ($classMethodNode->returnType === null) {
156-
continue;
157-
}
158-
159-
foreach (Parser::getNodeTypes($classMethodNode->returnType) as &$nodeType) {
160-
if (Parser::isGenericClass($nodeType)) {
161-
$this->handleClass($nodeType, $concreteGenericsMap, $result);
162-
} else {
163-
Parser::setNodeType($nodeType, $concreteGenericsMap, $this->classFinder);
155+
if ($classMethodNode->returnType !== null) {
156+
foreach (Parser::getNodeTypes($classMethodNode->returnType) as &$nodeType) {
157+
if (Parser::isGenericClass($nodeType)) {
158+
$this->handleClass($nodeType, $concreteGenericsMap, $result);
159+
} else {
160+
Parser::setNodeType($nodeType, $concreteGenericsMap, $this->classFinder);
161+
}
162+
unset($nodeType);
164163
}
165-
unset($nodeType);
166164
}
167165
}
168166

@@ -214,7 +212,11 @@ private function handleClass(Node &$node, GenericParametersMap $genericParameter
214212
$genericClassFqn = Parser::getNodeName($node, $this->classFinder);
215213
if (!$this->genericClassCache->has($genericClassFqn)) {
216214
$genericClassFileContent = $this->classFinder->getFileContentByClassFqn($genericClassFqn);
217-
$genericClassAst = Parser::resolveNames(Parser::parse($genericClassFileContent));
215+
try {
216+
$genericClassAst = Parser::resolveNames(Parser::parse($genericClassFileContent));
217+
} catch (\Exception $exception) {
218+
throw new \RuntimeException(sprintf('Can\'t parse class "%s"', $genericClassFqn), $exception->getCode(), $exception);
219+
}
218220
$this->genericClassCache->set($genericClassFqn, new self($this->classFinder, $this->concreteClassCache, $this->genericClassCache, $genericClassAst));
219221
}
220222

src/Compiler/Parser.php

+67-17
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,13 @@
66
use PhpParser\Lexer\Emulative;
77
use PhpParser\Node;
88
use PhpParser\Node\Expr\ClassConstFetch;
9+
use PhpParser\Node\Expr\Instanceof_;
910
use PhpParser\Node\Expr\New_;
1011
use PhpParser\Node\Stmt\Class_;
12+
use PhpParser\Node\Stmt\ClassMethod;
13+
use PhpParser\Node\Stmt\Interface_;
14+
use PhpParser\Node\Stmt\Property;
15+
use PhpParser\Node\Stmt\Trait_;
1116
use PhpParser\NodeFinder;
1217
use PhpParser\NodeTraverser;
1318
use PhpParser\NodeVisitor\CloningVisitor;
@@ -84,7 +89,7 @@ public static function getNodeName(Node $node, ClassFinderInterface $classFinder
8489
case $node instanceof Node\Identifier:
8590
return (string)$node->name;
8691
default:
87-
throw new \TypeError(sprintf('Invalid node name class "%s"', get_class($node)));
92+
throw new \TypeError(sprintf('Invalid node name class "%s" for getNodeName()', get_class($node)));
8893
}
8994
}
9095

@@ -102,7 +107,7 @@ public static function setNodeName(Node &$node, string $type): void
102107
$node->name = $type;
103108
break;
104109
default:
105-
throw new \TypeError(sprintf('Invalid node name class "%s"', get_class($node)));
110+
throw new \TypeError(sprintf('Invalid node name class "%s" for setNodeName()', get_class($node)));
106111
}
107112
}
108113

@@ -141,7 +146,7 @@ public static function getNodeTypes(Node &$node): array
141146
case $node instanceof Node\UnionType:
142147
return $node->types;
143148
default:
144-
throw new \TypeError('Invalid node type');
149+
throw new \TypeError(sprintf('Invalid node type "%s" for getNodeTypes()', get_class($node)));
145150
}
146151
}
147152

@@ -176,37 +181,82 @@ public static function isGenericClass(Node $node): bool
176181
*/
177182
public static function hasGenericClassUsages(array $ast): bool
178183
{
179-
/** @var Class_ $classNode */
180-
$classNode = Parser::filterOne($ast, [Class_::class]);
181-
if ($classNode !== null) {
184+
/** @var Node\Stmt\ClassLike $classNode */
185+
$classNode = Parser::filterOne($ast, [Class_::class, Interface_::class, Trait_::class]);
186+
if ($classNode === null) {
187+
return false;
188+
}
182189

183-
if (Parser::isGenericClass($classNode)) {
184-
return false;
190+
if (Parser::isGenericClass($classNode)) {
191+
return false;
192+
}
193+
194+
if ($classNode instanceof Class_ || $classNode instanceof Interface_) {
195+
if ($classNode->extends !== null && Parser::isGenericClass($classNode->extends)) {
196+
return true;
185197
}
198+
}
186199

187-
if ($classNode->extends !== null) {
188-
if (Parser::isGenericClass($classNode->extends)) {
200+
if ($classNode instanceof Class_) {
201+
foreach ($classNode->implements as $implementNode) {
202+
if (Parser::isGenericClass($implementNode)) {
189203
return true;
190204
}
191205
}
206+
}
192207

193-
foreach ($classNode->implements as $implementNode) {
194-
if (Parser::isGenericClass($implementNode)) {
208+
/** @var Node\Stmt\TraitUse[] $traitUseNodes */
209+
$traitUseNodes = Parser::filter([$classNode], [Node\Stmt\TraitUse::class]);
210+
foreach ($traitUseNodes as $traitUseNode) {
211+
foreach ($traitUseNode->traits as $traitNode) {
212+
if (Parser::isGenericClass($traitNode)) {
195213
return true;
196214
}
197215
}
216+
}
198217

199-
/** @var Node\Stmt\TraitUse[] $traitUseNodes */
200-
$traitUseNodes = Parser::filter([$classNode], [Node\Stmt\TraitUse::class]);
201-
foreach ($traitUseNodes as $traitUseNode) {
202-
foreach ($traitUseNode->traits as $traitNode) {
203-
if (Parser::isGenericClass($traitNode)) {
218+
/** @var Property[] $propertyNodes */
219+
$propertyNodes = Parser::filter([$classNode], [Property::class]);
220+
foreach ($propertyNodes as $propertyNode) {
221+
if ($propertyNode->type !== null) {
222+
foreach (Parser::getNodeTypes($propertyNode->type) as $nodeType) {
223+
if (Parser::isGenericClass($nodeType)) {
204224
return true;
205225
}
206226
}
207227
}
208228
}
209229

230+
/** @var ClassMethod[] $classMethodNodes */
231+
$classMethodNodes = Parser::filter([$classNode], [ClassMethod::class]);
232+
foreach ($classMethodNodes as $classMethodNode) {
233+
foreach ($classMethodNode->params as $param) {
234+
if ($param->type !== null) {
235+
foreach (Parser::getNodeTypes($param->type) as $nodeType) {
236+
if (Parser::isGenericClass($nodeType)) {
237+
return true;
238+
}
239+
}
240+
}
241+
}
242+
243+
if ($classMethodNode->returnType !== null) {
244+
foreach (Parser::getNodeTypes($classMethodNode->returnType) as $nodeType) {
245+
if (Parser::isGenericClass($nodeType)) {
246+
return true;
247+
}
248+
}
249+
}
250+
}
251+
252+
/** @var Instanceof_[] $newExprNodes */
253+
$instanceofExprNodes = Parser::filter([$classNode], [Instanceof_::class]);
254+
foreach ($instanceofExprNodes as $instanceofExprNode) {
255+
if (Parser::isGenericClass($instanceofExprNode->class)) {
256+
return true;
257+
}
258+
}
259+
210260
/** @var New_[] $newExprNodes */
211261
$newExprNodes = Parser::filter($ast, [New_::class]);
212262
foreach ($newExprNodes as $newExprNode) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Test\Command;
4+
5+
use Test\Generic\Box;
6+
use Test\Entity\Bird;
7+
use Test\Entity\Cat;
8+
9+
class Usage extends Box<Bird, Cat, int>
10+
{
11+
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php
2+
3+
namespace Test\Generic;
4+
5+
class Box<BoxA,BoxB,BoxC> {
6+
7+
private ?BoxA $content = null;
8+
9+
public function setContent(BoxB $content): BoxC {
10+
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Test\Command;
4+
5+
use Test\Generic\Box;
6+
use Test\Entity\Bird;
7+
use Test\Entity\Cat;
8+
class Usage extends \Test\Generic\BoxForTestEntityBirdAndTestEntityCatAndInt
9+
{
10+
}

0 commit comments

Comments
 (0)