Skip to content

Commit 4142e22

Browse files
authored
Merge pull request #90 from othercorey/resolve-class-string
Added support for class-string and class-string<Type> types
2 parents 28517b9 + df9143d commit 4142e22

File tree

4 files changed

+174
-1
lines changed

4 files changed

+174
-1
lines changed

src/TypeResolver.php

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use ArrayIterator;
1717
use InvalidArgumentException;
1818
use phpDocumentor\Reflection\Types\Array_;
19+
use phpDocumentor\Reflection\Types\ClassString;
1920
use phpDocumentor\Reflection\Types\Collection;
2021
use phpDocumentor\Reflection\Types\Compound;
2122
use phpDocumentor\Reflection\Types\Context;
@@ -66,6 +67,7 @@ final class TypeResolver
6667
*/
6768
private $keywords = [
6869
'string' => Types\String_::class,
70+
'class-string' => Types\ClassString::class,
6971
'int' => Types\Integer::class,
7072
'integer' => Types\Integer::class,
7173
'bool' => Types\Boolean::class,
@@ -221,7 +223,11 @@ private function parseTypes(ArrayIterator $tokens, Context $context, int $parser
221223

222224
$classType = array_pop($types);
223225
if ($classType !== null) {
224-
$types[] = $this->resolveCollection($tokens, $classType, $context);
226+
if ((string)$classType === 'class-string') {
227+
$types[] = $this->resolveClassString($tokens, $context);
228+
} else {
229+
$types[] = $this->resolveCollection($tokens, $classType, $context);
230+
}
225231
}
226232

227233
$tokens->next();
@@ -386,6 +392,36 @@ private function resolveTypedObject(string $type, ?Context $context = null) : Ob
386392
return new Object_($this->fqsenResolver->resolve($type, $context));
387393
}
388394

395+
/**
396+
* Resolves class string
397+
*/
398+
private function resolveClassString(ArrayIterator $tokens, Context $context) : Type
399+
{
400+
$tokens->next();
401+
402+
$classType = $this->parseTypes($tokens, $context, self::PARSER_IN_COLLECTION_EXPRESSION);
403+
404+
if (!$classType instanceof Object_ || $classType->getFqsen() === null) {
405+
throw new RuntimeException(
406+
$classType . ' is not a class string'
407+
);
408+
}
409+
410+
if ($tokens->current() !== '>') {
411+
if (empty($tokens->current())) {
412+
throw new RuntimeException(
413+
'class-string: ">" is missing'
414+
);
415+
}
416+
417+
throw new RuntimeException(
418+
'Unexpected character "' . $tokens->current() . '", ">" is missing'
419+
);
420+
}
421+
422+
return new ClassString($classType->getFqsen());
423+
}
424+
389425
/**
390426
* Resolves the collection values and keys
391427
*

src/Types/ClassString.php

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link http://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Reflection\Types;
15+
16+
use phpDocumentor\Reflection\Fqsen;
17+
use phpDocumentor\Reflection\Type;
18+
19+
/**
20+
* Value Object representing the type 'string'.
21+
*/
22+
final class ClassString implements Type
23+
{
24+
/** @var Fqsen|null */
25+
private $fqsen;
26+
27+
/**
28+
* Initializes this representation of a class string with the given Fqsen.
29+
*/
30+
public function __construct(?Fqsen $fqsen = null)
31+
{
32+
$this->fqsen = $fqsen;
33+
}
34+
35+
/**
36+
* Returns the FQSEN associated with this object.
37+
*/
38+
public function getFqsen() : ?Fqsen
39+
{
40+
return $this->fqsen;
41+
}
42+
43+
/**
44+
* Returns a rendered output of the Type as it would be used in a DocBlock.
45+
*/
46+
public function __toString() : string
47+
{
48+
if ($this->fqsen === null) {
49+
return 'class-string';
50+
} else {
51+
return 'class-string<' . (string)$this->fqsen . '>';
52+
}
53+
}
54+
}

tests/unit/TypeResolverTest.php

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Mockery as m;
1717
use phpDocumentor\Reflection\Types\Array_;
1818
use phpDocumentor\Reflection\Types\Boolean;
19+
use phpDocumentor\Reflection\Types\ClassString;
1920
use phpDocumentor\Reflection\Types\Compound;
2021
use phpDocumentor\Reflection\Types\Context;
2122
use phpDocumentor\Reflection\Types\Iterable_;
@@ -59,6 +60,30 @@ public function testResolvingKeywords(string $keyword, string $expectedClass) :
5960
$this->assertInstanceOf($expectedClass, $resolvedType);
6061
}
6162

63+
/**
64+
* @uses \phpDocumentor\Reflection\Types\Context
65+
* @uses \phpDocumentor\Reflection\Types\Object_
66+
* @uses \phpDocumentor\Reflection\Types\String_
67+
*
68+
* @covers ::__construct
69+
* @covers ::resolve
70+
* @covers ::<private>
71+
*
72+
* @dataProvider provideClassStrings
73+
*/
74+
public function testResolvingClassStrings(string $classString, bool $throwsException) : void
75+
{
76+
$fixture = new TypeResolver();
77+
78+
if ($throwsException) {
79+
$this->expectException('RuntimeException');
80+
}
81+
82+
$resolvedType = $fixture->resolve($classString, new Context(''));
83+
84+
$this->assertInstanceOf(ClassString::class, $resolvedType);
85+
}
86+
6287
/**
6388
* @uses \phpDocumentor\Reflection\Types\Context
6489
* @uses \phpDocumentor\Reflection\Types\Object_
@@ -634,6 +659,7 @@ public function provideKeywords() : array
634659
{
635660
return [
636661
['string', Types\String_::class],
662+
['class-string', Types\ClassString::class],
637663
['int', Types\Integer::class],
638664
['integer', Types\Integer::class],
639665
['float', Types\Float_::class],
@@ -657,6 +683,20 @@ public function provideKeywords() : array
657683
];
658684
}
659685

686+
/**
687+
* Returns a list of class string types and whether they throw an exception.
688+
*
689+
* @return (string|bool)[][]
690+
*/
691+
public function provideClassStrings() : array
692+
{
693+
return [
694+
['class-string<\phpDocumentor\Reflection>', false],
695+
['class-string<\phpDocumentor\Reflection\DocBlock>', false],
696+
['class-string<string>', true],
697+
];
698+
}
699+
660700
/**
661701
* Provides a list of FQSENs to test the resolution patterns with.
662702
*

tests/unit/Types/ClassStringTest.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* This file is part of phpDocumentor.
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*
11+
* @link http://phpdoc.org
12+
*/
13+
14+
namespace phpDocumentor\Reflection\Types;
15+
16+
use phpDocumentor\Reflection\Fqsen;
17+
use PHPUnit\Framework\TestCase;
18+
19+
/**
20+
* @coversDefaultClass \phpDocumentor\Reflection\Types\ClassString
21+
*/
22+
class ClassStringTest extends TestCase
23+
{
24+
/**
25+
* @dataProvider provideClassStrings
26+
* @covers ::__toString
27+
*/
28+
public function testClassStringStringifyCorrectly(ClassString $array, string $expectedString) : void
29+
{
30+
$this->assertSame($expectedString, (string) $array);
31+
}
32+
33+
/**
34+
* @return mixed[]
35+
*/
36+
public function provideClassStrings() : array
37+
{
38+
return [
39+
'generic clss string' => [new ClassString(), 'class-string'],
40+
'typed class string' => [new ClassString(new Fqsen('\Foo\Bar')), 'class-string<\Foo\Bar>'],
41+
];
42+
}
43+
}

0 commit comments

Comments
 (0)