1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* |
5
|
|
|
* This file is part of the Apix Project. |
6
|
|
|
* |
7
|
|
|
* (c) Franck Cassedanne <franck at ouarz.net> |
8
|
|
|
* |
9
|
|
|
* @license http://opensource.org/licenses/BSD-3-Clause New BSD License |
10
|
|
|
* |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace Apix; |
14
|
|
|
|
15
|
|
|
class Reflection |
16
|
|
|
{ |
17
|
|
|
/** |
18
|
|
|
* @var string |
19
|
|
|
*/ |
20
|
|
|
protected $prefix; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Constructor |
24
|
|
|
* |
25
|
|
|
* @param string|null $prefix [optional default:null] |
26
|
|
|
*/ |
27
|
|
|
public function __construct($prefix='null') |
28
|
|
|
{ |
29
|
|
|
$this->prefix = $prefix; |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Returns prefix |
34
|
|
|
* |
35
|
|
|
* @return string |
36
|
|
|
*/ |
37
|
|
|
public function getPrefix() |
38
|
|
|
{ |
39
|
|
|
return $this->prefix; |
40
|
|
|
} |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* Returns the PHPDoc string |
44
|
|
|
* |
45
|
|
|
* @param \Reflection|string $mix A reflection object or a PHPDoc string |
46
|
|
|
* @return array |
47
|
|
|
*/ |
48
|
|
|
// public function getPhpDocString($mix) |
|
|
|
|
49
|
|
|
// { |
50
|
|
|
// return $mix instanceOf \Reflector ? $mix->getDocComment() : $mix; |
|
|
|
|
51
|
|
|
// } |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* Extract PHPDOCs |
55
|
|
|
* |
56
|
|
|
* @param \Reflection|string $mix A reflection object or a PHPDoc string |
57
|
|
|
* @param array|null $requireds An array of param name that are required. |
58
|
|
|
* @return array |
59
|
|
|
*/ |
60
|
|
|
public static function parsePhpDoc($mix, array $requireds=null) |
61
|
|
|
{ |
62
|
|
|
if ($mix instanceof \Reflector) { |
63
|
|
|
$doc = $mix->getDocComment(); |
64
|
|
|
$requireds = self::getRequiredParams($mix); |
65
|
|
|
} else { |
66
|
|
|
$doc = $mix; |
67
|
|
|
} |
68
|
|
|
|
69
|
|
|
$docs = array(); |
70
|
|
|
|
71
|
|
|
// 1st - remove /*, *, */ from all the lines |
72
|
|
|
$doc = substr($doc, 3, -2); |
73
|
|
|
|
74
|
|
|
// 2. remove the carrier returns |
75
|
|
|
#$pattern = '/\r?\n *\* */'; |
|
|
|
|
76
|
|
|
|
77
|
|
|
// does 1. + 2. BUT not too efficiently! |
78
|
|
|
#$pattern = '%(\r?\n(?! \* ?@))?^(/\*\*\r?\n \* | \*/| \* ?)%m'; |
|
|
|
|
79
|
|
|
|
80
|
|
|
// same as 2. BUT keep the carrier returns in. |
81
|
|
|
// $pattern = '@(\r+|\t+)? *\* *@'; |
|
|
|
|
82
|
|
|
$pattern = '@(\r+|\t+)? +\*\s?@'; |
83
|
|
|
|
84
|
|
|
$str = preg_replace($pattern, '', $doc); |
85
|
|
|
|
86
|
|
|
#$lines =array_map('trim',explode(PHP_EOL, $str)); |
|
|
|
|
87
|
|
|
$lines = preg_split('@\r?\n|\r@', $str, null, PREG_SPLIT_NO_EMPTY); |
88
|
|
|
|
89
|
|
|
// extract the short decription (title) |
90
|
|
|
$docs['title'] = array_shift($lines); |
91
|
|
|
|
92
|
|
|
// extract the long description |
93
|
|
|
$docs['description'] = ''; |
94
|
|
|
foreach ($lines as $i => $line) { |
95
|
|
|
if (strlen(trim($line)) && strpos($line, '@') !== 0) { |
96
|
|
|
$docs['description'] .= $docs['description'] ? PHP_EOL . $line : $line; |
97
|
|
|
unset($lines[$i]); |
98
|
|
|
} else break; |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
// do all the "@entries" |
102
|
|
|
preg_match_all( |
103
|
|
|
'/@(?P<key>[\w_]+)\s+(?P<value>.*?)\s*(?=$|@[\w_]+\s)/s', |
104
|
|
|
$str, |
105
|
|
|
$lines |
106
|
|
|
); |
107
|
|
|
|
108
|
|
|
foreach ($lines['value'] as $i => $v) { |
109
|
|
|
$grp = $lines['key'][$i]; |
110
|
|
|
|
111
|
|
|
if ($grp == 'param' || $grp == 'global') { |
112
|
|
|
// "@param string $param description of param" |
113
|
|
|
preg_match('/(?P<t>\S+)\s+\$(?P<name>\S+)(?P<d>\s+(?:[\w\W_\s]*))?/', $v, $m); |
114
|
|
|
|
115
|
|
|
$t = $grp == 'param' ? 'params' : 'globals'; |
116
|
|
|
$docs[$t][$m['name']] = array( |
117
|
|
|
'type' => $m['t'], |
118
|
|
|
'name' => $m['name'], |
119
|
|
|
'description' => isset($m['d']) |
120
|
|
|
? trim($m['d']) |
121
|
|
|
: null, |
122
|
|
|
'required' => isset($requireds) |
123
|
|
|
&& in_array($m['name'], $requireds) |
124
|
|
|
); |
125
|
|
|
} else { |
126
|
|
|
// other @entries as group |
127
|
|
|
$docs[$grp][] = $v; |
128
|
|
|
} |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
if (isset($docs['return'])) { |
132
|
|
|
$returns = array(); |
133
|
|
|
foreach ($docs['return'] as $v) { |
134
|
|
|
// preg_match('/(?P<t>\S+)(?P<d>\s+(?:.+))?(?P<x>(?:.|\s)*)/', $v, $m); // with extar |
|
|
|
|
135
|
|
|
preg_match('/(?P<t>\S+)(?P<d>\s+(?:.|\s)*)/', $v, $m); |
136
|
|
|
if ( isset($m['t']) ) { |
137
|
|
|
$returns[] = array( |
138
|
|
|
'type' => $m['t'], |
139
|
|
|
'description' => trim($m['d']), |
140
|
|
|
// 'extra' => trim($m['x']), |
|
|
|
|
141
|
|
|
); |
142
|
|
|
} |
143
|
|
|
} |
144
|
|
|
$docs['return'] = array( $returns ); |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
// reduce group |
148
|
|
|
foreach ($docs as $key => $value) { |
149
|
|
|
if ( |
150
|
|
|
$key !== 'params' && $key !== 'globals' && $key !== 'methods' |
151
|
|
|
) { |
152
|
|
|
if (is_array($value) && count($value) == 1) { |
153
|
|
|
$docs[$key] = reset( $docs[$key] ); |
154
|
|
|
} |
155
|
|
|
} |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
return $docs; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Returns the required parameters |
163
|
|
|
* |
164
|
|
|
* @param \ReflectionFunctionAbstract $ref A reflected method/function to introspect |
165
|
|
|
* @return array The array of required parameters |
166
|
|
|
*/ |
167
|
|
|
public static function getRequiredParams(\ReflectionFunctionAbstract $ref) |
168
|
|
|
{ |
169
|
|
|
$params = array(); |
170
|
|
|
foreach ($ref->getParameters() as $param) { |
171
|
|
|
$name = $param->getName(); |
172
|
|
|
if ( !$param->isOptional() ) { |
173
|
|
|
$params[] = $name; |
174
|
|
|
} |
175
|
|
|
} |
176
|
|
|
|
177
|
|
|
return $params; |
178
|
|
|
} |
179
|
|
|
|
180
|
|
|
/** |
181
|
|
|
* Extract source code |
182
|
|
|
* |
183
|
|
|
* @param \Reflector $ref |
184
|
|
|
* @return array |
185
|
|
|
*/ |
186
|
|
|
public static function getSource(\Reflector $ref) |
187
|
|
|
{ |
188
|
|
|
if( !file_exists( $ref->getFileName() ) ) return false; |
|
|
|
|
189
|
|
|
|
190
|
|
|
$start_offset = $ref->getStartLine(); |
|
|
|
|
191
|
|
|
$end_offset = $ref->getEndLine()-$start_offset; |
|
|
|
|
192
|
|
|
|
193
|
|
|
return join('', |
194
|
|
|
array_slice( |
195
|
|
|
file($ref->getFileName()), |
|
|
|
|
196
|
|
|
$start_offset-1, |
197
|
|
|
$end_offset+1 |
198
|
|
|
) |
199
|
|
|
); |
200
|
|
|
} |
201
|
|
|
|
202
|
|
|
} |
203
|
|
|
|
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.