-
-
Notifications
You must be signed in to change notification settings - Fork 230
/
Copy pathParser.php
151 lines (137 loc) · 3.7 KB
/
Parser.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
<?php namespace ParaTest\Parser;
class Parser
{
/**
* The path to the source file to parse
*
* @var string
*/
private $path;
/**
* @var \ReflectionClass
*/
private $refl;
/**
* A pattern for matching namespace syntax
*
* @var string
*/
private static $namespace = '/\bnamespace\b[\s]+([^;]+);/';
/**
* A pattern for matching class syntax
*
* @var string
*/
private static $class = '/\bclass\b/';
/**
* A pattern for matching class syntax and extension
* defaulting to ungreedy matches, case insensitivity, and
* dot matches all
*
* @var string
*/
private static $className = '/\bclass\b\s+([^\s]+)\s+extends/Usi';
/**
* Matches a test method beginning with the conventional "test"
* word
*
* @var string
*/
private static $testName = '/^test/';
/**
* A pattern for matching test methods that use the @test annotation
*
* @var string
*/
private static $testAnnotation = '/@test\b/';
public function __construct($srcPath)
{
if(!file_exists($srcPath))
throw new \InvalidArgumentException("file not found");
$this->path = $srcPath;
$class = $this->getClassName();
require_once($this->path);
$this->refl = new \ReflectionClass($class);
}
/**
* Returns the fully constructed class
* with methods or null if the class is abstract
*
* @return null|ParsedClass
*/
public function getClass()
{
return ($this->refl->isAbstract())
? null
: new ParsedClass(
$this->refl->getDocComment(),
$this->refl->getName(),
$this->refl->getNamespaceName(),
$this->getMethods());
}
/**
* Return all test methods present in the file
*
* @return array
*/
private function getMethods()
{
$tests = array();
$methods = $this->refl->getMethods(\ReflectionMethod::IS_PUBLIC);
foreach($methods as $method) {
if(preg_match(self::$testName, $method->getName()) || preg_match(self::$testAnnotation, $method->getDocComment()))
$tests[] = new ParsedFunction($method->getDocComment(), 'public', $method->getName());
}
return $tests;
}
/**
* Return the class name of the class contained
* in the file
*
* @return string
*/
private function getClassName()
{
$class = str_replace('.php', '', basename($this->path));
$class = $this->parseClassName($class);
$namespace = $this->getNamespace();
if($namespace)
$class = $namespace . '\\' . $class;
return $class;
}
/**
* Reads just enough of the source file to
* get the class name
*
* @param $fallbackClassName
* @return mixed
*/
private function parseClassName($fallbackClassName)
{
$handle = fopen($this->path, 'r');
while($line = fgets($handle)) {
if(preg_match(self::$className, $line, $matches))
return $matches[1];
}
fclose($handle);
return $fallbackClassName;
}
/**
* Reads just enough of the source file to get the namespace
* of the source file
*
* @return string
*/
private function getNamespace()
{
$handle = fopen($this->path, 'r');
while($line = fgets($handle)) {
if(preg_match(self::$namespace, $line, $matches))
return $matches[1];
if(preg_match(self::$class, $line))
break;
}
fclose($handle);
return '';
}
}