Skip to content

Commit d415ee8

Browse files
committed
Update and Document TypeResolver and FqsenResolver
1 parent 1c9fdcd commit d415ee8

16 files changed

+307
-123
lines changed

README.md

Lines changed: 157 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,160 @@ properties and class constants but also functions and global constants.
1010

1111
This package provides two Resolvers that are capable of
1212

13-
1. determining the Type of a given expression and/or resolve any class names, and
14-
2. resolve any partial Structural Element Names into Fully Qualified Structural Element names
13+
1. Returning a series of Value Object for given expression while resolving any partial class names, and
14+
2. Returning an FQSEN object after resolving any partial Structural Element Names into Fully Qualified Structural
15+
Element names.
16+
17+
## Installing
18+
19+
The easiest way to install this library is with [Composer](http://getcomposer.org) using the following command:
20+
21+
$ composer require phpdocumentor/type-resolver
22+
23+
## Examples
24+
25+
Ready to dive in and don't want to read through all that text below? Just consult the [examples](examples) folder and
26+
check which type of action that your want to accomplish.
27+
28+
## On Types and Element Names
29+
30+
This component can be used in one of two ways
31+
32+
1. To resolve a Type or
33+
2. To resolve a Fully Qualified Structural Element Name
34+
35+
The big difference between these two is in the number of things it can resolve.
36+
37+
The TypeResolver can resolve:
38+
39+
- a php primitive or pseudo-primitive such as a string or void (`@var string` or `@return void`).
40+
- a composite such as an array of string (`@var string[]`).
41+
- a compound such as a string or integer (`@var string|integer`).
42+
- an object or interface such as the TypeResolver class (`@var TypeResolver`
43+
or `@var \phpDocumentor\Reflection\TypeResolver`)
44+
45+
> please note that if you want to pass partial class names that additional steps are necessary, see the
46+
> chapter `Resolving partial classes and FQSENs` for more information.
47+
48+
Where the FqsenResolver can resolve:
49+
50+
- Constant expressions (i.e. `@see \MyNamespace\MY_CONSTANT`)
51+
- Function expressions (i.e. `@see \MyNamespace\myFunction()`)
52+
- Class expressions (i.e. `@see \MyNamespace\MyClass`)
53+
- Interface expressions (i.e. `@see \MyNamespace\MyInterface`)
54+
- Trait expressions (i.e. `@see \MyNamespace\MyTrait`)
55+
- Class constant expressions (i.e. `@see \MyNamespace\MyClass::MY_CONSTANT`)
56+
- Property expressions (i.e. `@see \MyNamespace\MyClass::$myProperty`)
57+
- Method expressions (i.e. `@see \MyNamespace\MyClass::myMethod()`)
58+
59+
## Resolving a type
60+
61+
In order to resolve a type you will have to instantiate the class `\phpDocumentor\Reflection\TypeResolver`
62+
and call its `resolve` method like this:
63+
64+
$typeResolver = new \phpDocumentor\Reflection\TypeResolver();
65+
$type = $typeResolver->resolve('string|integer');
66+
67+
In this example you will receive a Value Object of class `\phpDocumentor\Reflection\Types\Compound` that has two
68+
elements, one of type `\phpDocumentor\Reflection\Types\String_` and one of type
69+
`\phpDocumentor\Reflection\Types\Integer`.
70+
71+
The real power of this resolver is in its capability to expand partial class names into fully qualified class names; but
72+
in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will inform the resolver
73+
in which namespace the given expression occurs and which namespace aliases (or imports) apply.
74+
75+
## Resolving an FQSEN
76+
77+
A Fully Qualified Structural Element Name is a reference to another element in your code bases and can be resolved using
78+
the `\phpDocumentor\Reflection\FqsenResolver` class' `resolve` method, like this:
79+
80+
$fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver();
81+
$fqsen = $fqsenResolver->resolve('\phpDocumentor\Reflection\FqsenResolver::resolve()');
82+
83+
In this example we resolve a Fully Qualified Structural Element Name (meaning that it includes the full namespace, class
84+
name and element name) and receive a Value Object of type `\phpDocumentor\Reflection\Fqsen`.
85+
86+
The real power of this resolver is in its capability to expand partial element names into Fully Qualified Structural
87+
Element Names; but in order to do that we need an additional `\phpDocumentor\Reflection\Types\Context` class that will
88+
inform the resolver in which namespace the given expression occurs and which namespace aliases (or imports) apply.
89+
90+
## Resolving partial Classes and Structural Element Names
91+
92+
Perhaps the best feature of this library is that it knows how to resolve partial class names into fully qualified class
93+
names.
94+
95+
For example, you have this file:
96+
97+
```php
98+
<?php
99+
namespace My\Example;
100+
101+
use phpDocumentor\Reflection\Types;
102+
103+
class Classy
104+
{
105+
/**
106+
* @var Types\Context
107+
* @see Classy::otherFunction()
108+
*/
109+
public function __construct($context) {}
110+
111+
public function otherFunction(){}
112+
}
113+
```
114+
115+
Suppose that you would want to resolve (and expand) the type in the `@var` tag and the element name in the `@see` tag.
116+
For the resolvers to know how to expand partial names you have to provide a bit of _Context_ for them by instantiating
117+
a new class named `\phpDocumentor\Reflection\Types\Context` with the name of the namespace and the aliases that are in
118+
play.
119+
120+
### Creating a Context
121+
122+
You can do this by manually creating a Context like this:
123+
124+
$context = new \phpDocumentor\Reflection\Types\Context(
125+
'\My\Example',
126+
[ 'Types' => '\phpDocumentor\Reflection\Types']
127+
);
128+
129+
Or by using the `\phpDocumentor\Reflection\Types\ContextFactory` to instantiate a new context based on a Reflector
130+
object or by providing the namespace that you'd like to extract and the source code of the file in which the given
131+
type expression occurs.
132+
133+
$contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
134+
$context = $contextFactory->createFromReflector(new ReflectionMethod('\My\Example\Classy', '__construct'));
135+
136+
or
137+
138+
$contextFactory = new \phpDocumentor\Reflection\Types\ContextFactory();
139+
$context = $contextFactory->createForNamespace('\My\Example', file_get_contents('My/Example/Classy.php'));
140+
141+
### Using the Context
142+
143+
After you have obtained a Context it is just a matter of passing it along with the `resolve` method of either Resolver
144+
class as second argument and the Resolvers will take this into account when resolving partial names.
145+
146+
To obtain the resolved class name for the `@var` tag in the example above you can do:
147+
148+
$typeResolver = new \phpDocumentor\Reflection\TypeResolver();
149+
$type = $typeResolver->resolve('Types\Context');
150+
151+
When you do this you will receive an object of class `\phpDocumentor\Reflection\Types\Object_` for which you can call
152+
the `getFqsen` method to receive a Value Object that represents the complete FQSEN. So that would be
153+
`phpDocumentor\Reflection\Types\Context`.
154+
155+
> Why is the FQSEN wrapped in another object `Object_`?
156+
>
157+
> The resolve method of the TypeResolver only returns object with the interface `Type` and the FQSEN is a common
158+
> type that does not represent a Type. Also: in some cases a type can represent an "Untyped Object", meaning that it
159+
> is an object (signified by the `object` keyword) but does not refer to a specific element using an FQSEN.
160+
161+
Another example is on how to resolve the FQSEN of a method as can be seen with the `@see` tag in the example above. To
162+
resolve that you can do the following:
163+
164+
$fqsenResolver = new \phpDocumentor\Reflection\FqsenResolver();
165+
$type = $fqsenResolver->resolve('Classy::otherFunction()');
166+
167+
Because Classy is a Class in the current namespace its FQSEN will have the `My\Example` namespace and by calling the
168+
`resolve` method of the FQSEN Resolver you will receive an `Fqsen` object that refers to
169+
`\My\Example\Classy::otherFunction()`.
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
<?php
22

3-
use phpDocumentor\Reflection\Types\Resolver;
3+
use phpDocumentor\Reflection\TypeResolver;
44

55
require '../vendor/autoload.php';
66

7-
$typeResolver = new Resolver();
7+
$typeResolver = new TypeResolver();
88

99
// Will yield an object of type phpDocumentor\Types\Compound
10-
var_export($typeResolver->resolveType('string|integer'));
10+
var_export($typeResolver->resolve('string|integer'));
1111

1212
// Will return the string "string|int"
13-
var_dump((string)$typeResolver->resolveType('string|integer'));
13+
var_dump((string)$typeResolver->resolve('string|integer'));

examples/02-resolving-classes.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
<?php
22

33
use phpDocumentor\Reflection\Types\Context;
4-
use phpDocumentor\Reflection\Types\Resolver;
4+
use phpDocumentor\Reflection\TypeResolver;
55

66
require '../vendor/autoload.php';
77

8-
$typeResolver = new Resolver();
8+
$typeResolver = new TypeResolver();
99

1010
// Will use the namespace and aliases to resolve to \phpDocumentor\Types\Resolver|Mockery\MockInterface
1111
$context = new Context('\phpDocumentor', [ 'm' => 'Mockery' ]);
12-
var_dump((string)$typeResolver->resolveType('Types\Resolver|m\MockInterface', $context));
12+
var_dump((string)$typeResolver->resolve('Types\Resolver|m\MockInterface', $context));
Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
<?php
22

33
use phpDocumentor\Reflection\Types\Context;
4-
use phpDocumentor\Reflection\Types\Resolver;
4+
use phpDocumentor\Reflection\FqsenResolver;
55

66
require '../vendor/autoload.php';
77

8-
$typeResolver = new Resolver();
8+
$fqsenResolver = new FqsenResolver();
99

1010
// Will use the namespace and aliases to resolve to a Fqsen object
1111
$context = new Context('\phpDocumentor\Types');
1212

1313
// Method named: \phpDocumentor\Types\Types\Resolver::resolveFqsen()
14-
var_dump((string)$typeResolver->resolveFqsen('Types\Resolver::resolveFqsen()', $context));
14+
var_dump((string)$fqsenResolver->resolve('Types\Resolver::resolveFqsen()', $context));
1515

1616
// Property named: \phpDocumentor\Types\Types\Resolver::$keyWords
17-
var_dump((string)$typeResolver->resolveFqsen('Types\Resolver::$keyWords', $context));
17+
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));
Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,30 @@
11
<?php
22

3+
use phpDocumentor\Reflection\FqsenResolver;
4+
use phpDocumentor\Reflection\TypeResolver;
35
use phpDocumentor\Reflection\Types\ContextFactory;
4-
use phpDocumentor\Reflection\Types\Resolver;
56

67
require '../vendor/autoload.php';
78
require 'Classy.php';
89

9-
$typeResolver = new Resolver();
10+
$typeResolver = new TypeResolver();
11+
$fqsenResolver = new FqsenResolver();
12+
1013
$contextFactory = new ContextFactory();
11-
$context = $contextFactory->createFromClassReflector(new ReflectionClass('My\\Example\\Classy'));
14+
$context = $contextFactory->createFromReflector(new ReflectionClass('My\\Example\\Classy'));
1215

1316
// Class named: \phpDocumentor\Reflection\Types\Resolver
14-
var_dump((string)$typeResolver->resolveType('Types\Resolver', $context));
17+
var_dump((string)$typeResolver->resolve('Types\Resolver', $context));
1518

1619
// String
17-
var_dump((string)$typeResolver->resolveType('string', $context));
20+
var_dump((string)$typeResolver->resolve('string', $context));
1821

1922
// Property named: \phpDocumentor\Reflection\Types\Resolver::$keyWords
20-
var_dump((string)$typeResolver->resolveFqsen('Types\Resolver::$keyWords', $context));
23+
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));
24+
25+
// Class named: \My\Example\string
26+
// - Shows the difference between the FqsenResolver and TypeResolver; the FqsenResolver will assume
27+
// that the given value is not a type but most definitely a reference to another element. This is
28+
// because conflicts between type keywords and class names can exist and if you know a reference
29+
// is not a type but an element you can force that keywords are resolved.
30+
var_dump((string)$fqsenResolver->resolve('string', $context));

examples/05-discovering-the-context-using-file-contents.php

Lines changed: 0 additions & 20 deletions
This file was deleted.
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<?php
2+
3+
use phpDocumentor\Reflection\FqsenResolver;
4+
use phpDocumentor\Reflection\TypeResolver;
5+
use phpDocumentor\Reflection\Types\ContextFactory;
6+
7+
require '../vendor/autoload.php';
8+
require 'Classy.php';
9+
10+
$typeResolver = new TypeResolver();
11+
$fqsenResolver = new FqsenResolver();
12+
13+
$contextFactory = new ContextFactory();
14+
$context = $contextFactory->createFromReflector(new ReflectionMethod('My\\Example\\Classy', '__construct'));
15+
16+
// Class named: \phpDocumentor\Reflection\Types\Resolver
17+
var_dump((string)$typeResolver->resolve('Types\Resolver', $context));
18+
19+
// String
20+
var_dump((string)$typeResolver->resolve('string', $context));
21+
22+
// Property named: \phpDocumentor\Reflection\Types\Resolver::$keyWords
23+
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));
24+
25+
// Class named: \My\Example\string
26+
// - Shows the difference between the FqsenResolver and TypeResolver; the FqsenResolver will assume
27+
// that the given value is not a type but most definitely a reference to another element. This is
28+
// because conflicts between type keywords and class names can exist and if you know a reference
29+
// is not a type but an element you can force that keywords are resolved.
30+
var_dump((string)$fqsenResolver->resolve('string', $context));
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
use phpDocumentor\Reflection\FqsenResolver;
4+
use phpDocumentor\Reflection\TypeResolver;
5+
use phpDocumentor\Reflection\Types\ContextFactory;
6+
7+
require '../vendor/autoload.php';
8+
9+
$typeResolver = new TypeResolver();
10+
$fqsenResolver = new FqsenResolver();
11+
12+
$contextFactory = new ContextFactory();
13+
$context = $contextFactory->createForNamespace('My\Example', file_get_contents('Classy.php'));
14+
15+
// Class named: \phpDocumentor\Reflection\Types\Resolver
16+
var_dump((string)$typeResolver->resolve('Types\Resolver', $context));
17+
18+
// String
19+
var_dump((string)$typeResolver->resolve('string', $context));
20+
21+
// Property named: \phpDocumentor\Reflection\Types\Resolver::$keyWords
22+
var_dump((string)$fqsenResolver->resolve('Types\Resolver::$keyWords', $context));

examples/Classy.php

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@
77

88
class Classy
99
{
10-
public function __construct()
10+
/**
11+
* @var Types\Context
12+
*/
13+
public function __construct($context)
1114
{
12-
1315
}
1416
}

phpunit.xml.dist

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,16 @@
22

33
<phpunit colors="true" strict="true" bootstrap="vendor/autoload.php">
44
<testsuites>
5-
<testsuite name="phpDocumentor\Reflection\DocBlock">
6-
<directory>./tests/</directory>
5+
<testsuite name="unit">
6+
<directory>./tests/unit</directory>
77
</testsuite>
88
</testsuites>
99
<filter>
1010
<whitelist>
1111
<directory suffix=".php">./src/</directory>
1212
</whitelist>
1313
<blacklist>
14+
<directory>./examples/</directory>
1415
<directory>./vendor/</directory>
1516
</blacklist>
1617
</filter>

0 commit comments

Comments
 (0)