Skip to content

Commit 68ed09d

Browse files
committed
Remove the dependency to doctrine/annotations: standalone package
1 parent 16861e8 commit 68ed09d

File tree

4 files changed

+234
-8
lines changed

4 files changed

+234
-8
lines changed

composer.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
}
1616
},
1717
"require": {
18-
"php": ">=5.3.0",
19-
"doctrine/annotations": "1.*"
18+
"php": ">=5.3.0"
2019
},
2120
"require-dev": {
2221
"phpunit/phpunit": "~4.6"

src/PhpDocReader/PhpDocReader.php

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace PhpDocReader;
44

5-
use Doctrine\Common\Annotations\PhpParser;
5+
use PhpDocReader\PhpParser\UseStatementParser;
66
use ReflectionParameter;
77
use ReflectionProperty;
88

@@ -14,9 +14,9 @@
1414
class PhpDocReader
1515
{
1616
/**
17-
* @var PhpParser
17+
* @var UseStatementParser
1818
*/
19-
private $phpParser;
19+
private $parser;
2020

2121
private $ignoredTypes = array(
2222
'bool',
@@ -45,7 +45,7 @@ class PhpDocReader
4545
*/
4646
public function __construct($ignorePhpDocErrors = false)
4747
{
48-
$this->phpParser = new PhpParser();
48+
$this->parser = new UseStatementParser();
4949
$this->ignorePhpDocErrors = $ignorePhpDocErrors;
5050
}
5151

@@ -99,7 +99,7 @@ public function getPropertyClass(ReflectionProperty $property)
9999
$loweredAlias = strtolower($alias);
100100

101101
// Retrieve "use" statements
102-
$uses = $this->phpParser->parseClass($property->getDeclaringClass());
102+
$uses = $this->parser->parseUseStatements($property->getDeclaringClass());
103103

104104
$found = false;
105105

@@ -207,7 +207,7 @@ public function getParameterClass(ReflectionParameter $parameter)
207207
$loweredAlias = strtolower($alias);
208208

209209
// Retrieve "use" statements
210-
$uses = $this->phpParser->parseClass($class);
210+
$uses = $this->parser->parseUseStatements($class);
211211

212212
$found = false;
213213

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
<?php
2+
3+
namespace PhpDocReader\PhpParser;
4+
5+
/**
6+
* Parses a file for namespaces/use/class declarations.
7+
*
8+
* Class taken and adapted from doctrine/annotations to avoid pulling the whole package.
9+
*
10+
* @author Fabien Potencier <fabien@symfony.com>
11+
* @author Christian Kaps <christian.kaps@mohiva.com>
12+
*/
13+
class TokenParser
14+
{
15+
/**
16+
* The token list.
17+
*
18+
* @var array
19+
*/
20+
private $tokens;
21+
22+
/**
23+
* The number of tokens.
24+
*
25+
* @var int
26+
*/
27+
private $numTokens;
28+
29+
/**
30+
* The current array pointer.
31+
*
32+
* @var int
33+
*/
34+
private $pointer = 0;
35+
36+
/**
37+
* @param string $contents
38+
*/
39+
public function __construct($contents)
40+
{
41+
$this->tokens = token_get_all($contents);
42+
43+
// The PHP parser sets internal compiler globals for certain things. Annoyingly, the last docblock comment it
44+
// saw gets stored in doc_comment. When it comes to compile the next thing to be include()d this stored
45+
// doc_comment becomes owned by the first thing the compiler sees in the file that it considers might have a
46+
// docblock. If the first thing in the file is a class without a doc block this would cause calls to
47+
// getDocBlock() on said class to return our long lost doc_comment. Argh.
48+
// To workaround, cause the parser to parse an empty docblock. Sure getDocBlock() will return this, but at least
49+
// it's harmless to us.
50+
token_get_all("<?php\n/**\n *\n */");
51+
52+
$this->numTokens = count($this->tokens);
53+
}
54+
55+
/**
56+
* Gets all use statements.
57+
*
58+
* @param string $namespaceName The namespace name of the reflected class.
59+
*
60+
* @return array A list with all found use statements.
61+
*/
62+
public function parseUseStatements($namespaceName)
63+
{
64+
$statements = array();
65+
while (($token = $this->next())) {
66+
if ($token[0] === T_USE) {
67+
$statements = array_merge($statements, $this->parseUseStatement());
68+
continue;
69+
}
70+
if ($token[0] !== T_NAMESPACE || $this->parseNamespace() != $namespaceName) {
71+
continue;
72+
}
73+
74+
// Get fresh array for new namespace. This is to prevent the parser to collect the use statements
75+
// for a previous namespace with the same name. This is the case if a namespace is defined twice
76+
// or if a namespace with the same name is commented out.
77+
$statements = array();
78+
}
79+
80+
return $statements;
81+
}
82+
83+
/**
84+
* Gets the next non whitespace and non comment token.
85+
*
86+
* @param boolean $docCommentIsComment If TRUE then a doc comment is considered a comment and skipped.
87+
* If FALSE then only whitespace and normal comments are skipped.
88+
*
89+
* @return array|null The token if exists, null otherwise.
90+
*/
91+
private function next($docCommentIsComment = true)
92+
{
93+
for ($i = $this->pointer; $i < $this->numTokens; $i++) {
94+
$this->pointer++;
95+
if ($this->tokens[$i][0] === T_WHITESPACE ||
96+
$this->tokens[$i][0] === T_COMMENT ||
97+
($docCommentIsComment && $this->tokens[$i][0] === T_DOC_COMMENT)) {
98+
99+
continue;
100+
}
101+
102+
return $this->tokens[$i];
103+
}
104+
105+
return null;
106+
}
107+
108+
/**
109+
* Parses a single use statement.
110+
*
111+
* @return array A list with all found class names for a use statement.
112+
*/
113+
private function parseUseStatement()
114+
{
115+
$class = '';
116+
$alias = '';
117+
$statements = array();
118+
$explicitAlias = false;
119+
while (($token = $this->next())) {
120+
$isNameToken = $token[0] === T_STRING || $token[0] === T_NS_SEPARATOR;
121+
if (!$explicitAlias && $isNameToken) {
122+
$class .= $token[1];
123+
$alias = $token[1];
124+
} elseif ($explicitAlias && $isNameToken) {
125+
$alias .= $token[1];
126+
} elseif ($token[0] === T_AS) {
127+
$explicitAlias = true;
128+
$alias = '';
129+
} elseif ($token === ',') {
130+
$statements[strtolower($alias)] = $class;
131+
$class = '';
132+
$alias = '';
133+
$explicitAlias = false;
134+
} elseif ($token === ';') {
135+
$statements[strtolower($alias)] = $class;
136+
break;
137+
} else {
138+
break;
139+
}
140+
}
141+
142+
return $statements;
143+
}
144+
145+
/**
146+
* Gets the namespace.
147+
*
148+
* @return string The found namespace.
149+
*/
150+
private function parseNamespace()
151+
{
152+
$name = '';
153+
while (($token = $this->next()) && ($token[0] === T_STRING || $token[0] === T_NS_SEPARATOR)) {
154+
$name .= $token[1];
155+
}
156+
157+
return $name;
158+
}
159+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
namespace PhpDocReader\PhpParser;
4+
5+
use SplFileObject;
6+
7+
/**
8+
* Parses a file for "use" declarations.
9+
*
10+
* Class taken and adapted from doctrine/annotations to avoid pulling the whole package.
11+
*
12+
* @author Fabien Potencier <fabien@symfony.com>
13+
* @author Christian Kaps <christian.kaps@mohiva.com>
14+
*/
15+
class UseStatementParser
16+
{
17+
/**
18+
* @return array A list with use statements in the form (Alias => FQN).
19+
*/
20+
public function parseUseStatements(\ReflectionClass $class)
21+
{
22+
if (false === $filename = $class->getFilename()) {
23+
return array();
24+
}
25+
26+
$content = $this->getFileContent($filename, $class->getStartLine());
27+
28+
if (null === $content) {
29+
return array();
30+
}
31+
32+
$namespace = preg_quote($class->getNamespaceName());
33+
$content = preg_replace('/^.*?(\bnamespace\s+' . $namespace . '\s*[;{].*)$/s', '\\1', $content);
34+
$tokenizer = new TokenParser('<?php ' . $content);
35+
36+
$statements = $tokenizer->parseUseStatements($class->getNamespaceName());
37+
38+
return $statements;
39+
}
40+
41+
/**
42+
* Gets the content of the file right up to the given line number.
43+
*
44+
* @param string $filename The name of the file to load.
45+
* @param integer $lineNumber The number of lines to read from file.
46+
*
47+
* @return string The content of the file.
48+
*/
49+
private function getFileContent($filename, $lineNumber)
50+
{
51+
if ( ! is_file($filename)) {
52+
return null;
53+
}
54+
55+
$content = '';
56+
$lineCnt = 0;
57+
$file = new SplFileObject($filename);
58+
while (!$file->eof()) {
59+
if ($lineCnt++ == $lineNumber) {
60+
break;
61+
}
62+
63+
$content .= $file->fgets();
64+
}
65+
66+
return $content;
67+
}
68+
}

0 commit comments

Comments
 (0)