1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* ApiModel class file. |
4
|
|
|
* |
5
|
|
|
* @author Qiang Xue <[email protected]> |
6
|
|
|
* @link http://www.yiiframework.com/ |
7
|
|
|
* @copyright 2008-2013 Yii Software LLC |
8
|
|
|
* @license http://www.yiiframework.com/license/ |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* ApiModel represents the documentation for the Yii framework. |
13
|
|
|
* @author Qiang Xue <[email protected]> |
14
|
|
|
* @package system.build |
15
|
|
|
* @since 1.0 |
16
|
|
|
*/ |
17
|
|
|
class ApiModel |
18
|
|
|
{ |
19
|
|
|
public $classes=array(); |
20
|
|
|
public $packages; |
21
|
|
|
|
22
|
|
|
private $_currentClass; |
23
|
|
|
|
24
|
|
|
public function build($sourceFiles) |
25
|
|
|
{ |
26
|
|
|
$this->findClasses($sourceFiles); |
27
|
|
|
$this->processClasses(); |
28
|
|
|
} |
29
|
|
|
|
30
|
|
|
/* |
31
|
|
|
* Calls checkSource for every file in $sourceFiles |
32
|
|
|
* @param array $sourceFiles array of source file path that we need to check |
33
|
|
|
*/ |
34
|
|
|
public function check($sourceFiles) |
35
|
|
|
{ |
36
|
|
|
echo "Checking PHPDoc @param in source files ...\n"; |
37
|
|
|
foreach($sourceFiles as $no=>$sourceFile) |
38
|
|
|
{ |
39
|
|
|
$this->checkSource($sourceFile); |
40
|
|
|
} |
41
|
|
|
echo "Done.\n\n"; |
42
|
|
|
} |
43
|
|
|
|
44
|
|
|
public function checkPackages($sourceFiles) |
45
|
|
|
{ |
46
|
|
|
echo "Checking PHPDoc @package in source files ...\n"; |
47
|
|
|
foreach($sourceFiles as $sourceFile) |
48
|
|
|
{ |
49
|
|
|
$fileContent = file($sourceFile); |
50
|
|
|
|
51
|
|
|
foreach($fileContent as $no=>$line) |
52
|
|
|
{ |
53
|
|
|
if( preg_match('/^\s*\*\s*@package\s*([\w\.]+)/', $line, $matches, PREG_OFFSET_CAPTURE) ) |
54
|
|
|
{ |
55
|
|
|
if( Yii::getPathOfAlias($matches[1][0]) == dirname($sourceFile)) |
56
|
|
|
continue; |
57
|
|
|
|
58
|
|
|
$docLine = $no + 1; |
59
|
|
|
$docName = $matches[1][0]; |
60
|
|
|
|
61
|
|
|
echo "ERROR.............: Package path not valid!\n"; |
62
|
|
|
echo "Source file.......: ".$sourceFile."\n"; |
63
|
|
|
echo "Parameter line....: ".$docLine."\n"; |
64
|
|
|
echo "Parameter value....: ".$docName."\n\n"; |
65
|
|
|
} |
66
|
|
|
} |
67
|
|
|
} |
68
|
|
|
echo "Done.\n\n"; |
69
|
|
|
} |
70
|
|
|
|
71
|
|
|
protected function findClasses($sourceFiles) |
72
|
|
|
{ |
73
|
|
|
$this->classes=array(); |
74
|
|
|
|
75
|
|
|
foreach($sourceFiles as $file) |
76
|
|
|
require_once($file); |
77
|
|
|
|
78
|
|
|
$classes=array_merge(get_declared_classes(),get_declared_interfaces()); |
79
|
|
|
foreach($classes as $class) |
80
|
|
|
{ |
81
|
|
|
$r=new ReflectionClass($class); |
82
|
|
|
if(in_array($r->getFileName(),$sourceFiles)) |
83
|
|
|
$this->classes[$class]=true; |
84
|
|
|
} |
85
|
|
|
ksort($this->classes); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
protected function processClasses() |
89
|
|
|
{ |
90
|
|
|
$this->packages=array(); |
91
|
|
|
foreach($this->classes as $class=>$value) |
92
|
|
|
{ |
93
|
|
|
$doc=$this->processClass(new ReflectionClass($class)); |
94
|
|
|
$this->classes[$class]=$doc; |
95
|
|
|
$this->packages[$doc->package][]=$class; |
96
|
|
|
} |
97
|
|
|
ksort($this->packages); |
98
|
|
|
|
99
|
|
|
// find out child classes for each class or interface |
100
|
|
|
foreach($this->classes as $class) |
101
|
|
|
{ |
102
|
|
|
if(isset($class->parentClasses[0])) |
103
|
|
|
{ |
104
|
|
|
$parent=$class->parentClasses[0]; |
105
|
|
|
if(isset($this->classes[$parent])) |
106
|
|
|
$this->classes[$parent]->subclasses[]=$class->name; |
107
|
|
|
} |
108
|
|
|
foreach($class->interfaces as $interface) |
109
|
|
|
{ |
110
|
|
|
if(isset($this->classes[$interface])) |
111
|
|
|
$this->classes[$interface]->subclasses[]=$class->name; |
112
|
|
|
} |
113
|
|
|
} |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
protected function processClass($class) |
117
|
|
|
{ |
118
|
|
|
$doc=new ClassDoc; |
119
|
|
|
$doc->name=$class->getName(); |
120
|
|
|
$doc->loadSource($class); |
121
|
|
|
$this->_currentClass=$doc->name; |
122
|
|
|
for($parent=$class;$parent=$parent->getParentClass();) |
123
|
|
|
$doc->parentClasses[]=$parent->getName(); |
124
|
|
|
foreach($class->getInterfaces() as $interface) |
125
|
|
|
$doc->interfaces[]=$interface->getName(); |
126
|
|
|
$doc->isInterface=$class->isInterface(); |
127
|
|
|
$doc->isAbstract=$class->isAbstract(); |
128
|
|
|
$doc->isFinal=$class->isFinal(); |
129
|
|
|
$doc->methods=$this->processMethods($class); |
130
|
|
|
$doc->properties=$this->processProperties($class); |
131
|
|
|
$doc->signature=($doc->isInterface?'interface ':'class ').$doc->name; |
132
|
|
|
if($doc->isFinal) |
133
|
|
|
$doc->signature='final '.$doc->signature; |
134
|
|
|
if($doc->isAbstract && !$doc->isInterface) |
135
|
|
|
$doc->signature='abstract '.$doc->signature; |
136
|
|
|
if(in_array('CComponent',$doc->parentClasses)) |
137
|
|
|
{ |
138
|
|
|
$doc->properties=array_merge($doc->properties,$this->processComponentProperties($class)); |
139
|
|
|
$doc->events=$this->processComponentEvents($class); |
140
|
|
|
} |
141
|
|
|
ksort($doc->properties); |
142
|
|
|
|
143
|
|
|
foreach($doc->properties as $property) |
144
|
|
|
{ |
145
|
|
|
if($property->isProtected) |
146
|
|
|
$doc->protectedPropertyCount++; |
147
|
|
|
else |
148
|
|
|
$doc->publicPropertyCount++; |
149
|
|
|
if(!$property->isInherited) |
150
|
|
|
$doc->nativePropertyCount++; |
151
|
|
|
} |
152
|
|
|
foreach($doc->methods as $method) |
153
|
|
|
{ |
154
|
|
|
if($method->isProtected) |
155
|
|
|
$doc->protectedMethodCount++; |
156
|
|
|
else |
157
|
|
|
$doc->publicMethodCount++; |
158
|
|
|
if(!$method->isInherited) |
159
|
|
|
$doc->nativeMethodCount++; |
160
|
|
|
} |
161
|
|
|
foreach($doc->events as $event) |
162
|
|
|
{ |
163
|
|
|
if(!$event->isInherited) |
164
|
|
|
$doc->nativeEventCount++; |
165
|
|
|
} |
166
|
|
|
$this->processComment($doc,$class->getDocComment()); |
167
|
|
|
|
168
|
|
|
return $doc; |
169
|
|
|
} |
170
|
|
|
|
171
|
|
|
protected function processComment($doc,$comment) |
172
|
|
|
{ |
173
|
|
|
$comment = strtr(trim(preg_replace('/^\s*\**( |\t)?/m','',trim($comment,'/'))),"\r",''); |
174
|
|
|
|
175
|
|
|
if( preg_match_all('/^\s*@(author|link|copyright|license|package).*/m',$comment ,$matches) ) |
176
|
|
|
{ |
177
|
|
|
$comment = strtr(trim(preg_replace('/^\s*@(author|link|copyright|license|package).*$/m', '', $comment)),"\r",''); |
178
|
|
|
$comment .= "\n"; |
179
|
|
|
foreach($matches[0] as $meta) |
180
|
|
|
$comment .= $meta."\n"; |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
if(preg_match('/^\s*@\w+/m',$comment,$matches,PREG_OFFSET_CAPTURE)) |
184
|
|
|
{ |
185
|
|
|
$meta=substr($comment,$matches[0][1]); |
186
|
|
|
$comment=trim(substr($comment,0,$matches[0][1])); |
187
|
|
|
} |
188
|
|
|
else |
189
|
|
|
$meta=''; |
190
|
|
|
if(($pos=strpos($comment,"\n"))!==false) |
191
|
|
|
$doc->introduction=$this->processDescription(substr($comment,0,$pos)); |
192
|
|
|
else |
193
|
|
|
$doc->introduction=$this->processDescription($comment); |
194
|
|
|
|
195
|
|
|
$doc->description=$this->processDescription($comment); |
196
|
|
|
|
197
|
|
|
$this->processTags($doc,$meta); |
198
|
|
|
} |
199
|
|
|
|
200
|
|
|
protected function processDescription($text) |
201
|
|
|
{ |
202
|
|
|
if(($text=trim($text))==='') |
203
|
|
|
return ''; |
204
|
|
|
$text=preg_replace_callback('/\{@include\s+([^\s\}]+)\s*\}/s',array($this,'processInclude'),$text); |
205
|
|
|
$text=preg_replace('/^(\r| |\t)*$/m',"<br/><br/>",$text); |
206
|
|
|
$text=preg_replace_callback('/<pre>(.*?)<\/pre>/is',array($this,'processCode'),$text); |
207
|
|
|
$text=preg_replace_callback('/\{@link\s+([^\s\}]+)(.*?)\}/s',array($this,'processLink'),$text); |
208
|
|
|
return $text; |
209
|
|
|
} |
210
|
|
|
|
211
|
|
|
protected function processCode($matches) |
212
|
|
|
{ |
213
|
|
|
$match=preg_replace('/<br\/><br\/>/','',$matches[1]); |
214
|
|
|
return "<pre>".htmlspecialchars($match)."</pre>"; |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
protected function resolveInternalUrl($url) |
218
|
|
|
{ |
219
|
|
|
$url=rtrim($url,'()'); |
220
|
|
|
if(($pos=strpos($url,'::'))!==false) |
221
|
|
|
{ |
222
|
|
|
$class=substr($url,0,$pos); |
223
|
|
|
$method=substr($url,$pos+2); |
224
|
|
|
} |
225
|
|
|
else if(isset($this->classes[$url])) |
226
|
|
|
return $url; |
227
|
|
|
else |
228
|
|
|
{ |
229
|
|
|
$class=$this->_currentClass; |
230
|
|
|
$method=$url; |
231
|
|
|
} |
232
|
|
|
return $this->getMethodUrl($class,$method); |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
protected function getMethodUrl($class,$method) |
236
|
|
|
{ |
237
|
|
|
if(!isset($this->classes[$class])) |
238
|
|
|
return ''; |
239
|
|
View Code Duplication |
if(method_exists($class,$method) || property_exists($class,$method)) |
|
|
|
|
240
|
|
|
return $class.'::'.$method; |
241
|
|
View Code Duplication |
if(method_exists($class,'get'.$method) || method_exists($class,'set'.$method)) |
|
|
|
|
242
|
|
|
return $class.'::'.$method; |
243
|
|
|
if(($parent=get_parent_class($class))!==false) |
244
|
|
|
return $this->getMethodUrl($parent,$method); |
245
|
|
|
else |
246
|
|
|
return ''; |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
protected function processLink($matches) |
250
|
|
|
{ |
251
|
|
|
$url=$matches[1]; |
252
|
|
|
if(($text=trim($matches[2]))==='') |
253
|
|
|
$text=$url; |
254
|
|
|
|
255
|
|
|
if(preg_match('/^(http|ftp):\/\//i',$url)) // an external URL |
256
|
|
|
return "<a href=\"$url\">$text</a>"; |
257
|
|
|
$url=$this->resolveInternalUrl($url); |
258
|
|
|
return $url===''?$text:'{{'.$url.'|'.$text.'}}'; |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
protected function processInclude($matches) |
262
|
|
|
{ |
263
|
|
|
$class=new ReflectionClass($this->_currentClass); |
264
|
|
|
$fileName=dirname($class->getFileName()).DIRECTORY_SEPARATOR.$matches[1]; |
265
|
|
|
if(is_file($fileName)) |
266
|
|
|
return file_get_contents($fileName); |
267
|
|
|
else |
268
|
|
|
return $matches[0]; |
269
|
|
|
} |
270
|
|
|
|
271
|
|
|
protected function processTags($object,$comment) |
272
|
|
|
{ |
273
|
|
|
$tags=preg_split('/^\s*@/m',$comment,-1,PREG_SPLIT_NO_EMPTY); |
274
|
|
|
foreach($tags as $tag) |
275
|
|
|
{ |
276
|
|
|
$segs=preg_split('/\s+/',trim($tag),2); |
277
|
|
|
$tagName=$segs[0]; |
278
|
|
|
$param=isset($segs[1])?trim($segs[1]):''; |
279
|
|
|
$tagMethod='tag'.ucfirst($tagName); |
280
|
|
|
if(method_exists($this,$tagMethod)) |
281
|
|
|
$this->$tagMethod($object,$param); |
282
|
|
|
else if(property_exists($object,$tagName)) |
283
|
|
|
$object->$tagName=$param; |
284
|
|
|
} |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
protected function processMethods($class) |
288
|
|
|
{ |
289
|
|
|
$methods=array(); |
290
|
|
|
foreach($class->getMethods() as $method) |
291
|
|
|
{ |
292
|
|
|
if($method->isPublic() || $method->isProtected()) |
293
|
|
|
{ |
294
|
|
|
$doc=$this->processMethod($class,$method); |
295
|
|
|
$methods[$doc->name]=$doc; |
296
|
|
|
} |
297
|
|
|
} |
298
|
|
|
ksort($methods); |
299
|
|
|
return $methods; |
300
|
|
|
} |
301
|
|
|
|
302
|
|
|
protected function processMethod($class,$method) |
303
|
|
|
{ |
304
|
|
|
$doc=new MethodDoc; |
305
|
|
|
$doc->name=$method->getName(); |
306
|
|
|
$doc->loadSource($method); |
307
|
|
|
$doc->definedBy=$method->getDeclaringClass()->getName(); |
308
|
|
|
$doc->isAbstract=$method->isAbstract(); |
309
|
|
|
$doc->isFinal=$method->isFinal(); |
310
|
|
|
$doc->isProtected=$method->isProtected(); |
311
|
|
|
$doc->isStatic=$method->isStatic(); |
312
|
|
|
$doc->isInherited=$doc->definedBy!==$class->getName(); |
313
|
|
|
|
314
|
|
|
$doc->input=array(); |
315
|
|
|
foreach($method->getParameters() as $param) |
316
|
|
|
{ |
317
|
|
|
$p=new ParamDoc; |
318
|
|
|
$p->name=$param->getName(); |
319
|
|
|
$p->isOptional=$param->isOptional(); |
320
|
|
|
if($param->isDefaultValueAvailable()) |
321
|
|
|
$p->defaultValue=$param->getDefaultValue(); |
322
|
|
|
$p->isPassedByReference=$param->isPassedByReference(); |
323
|
|
|
$doc->input[]=$p; |
324
|
|
|
} |
325
|
|
|
reset($doc->input); |
326
|
|
|
|
327
|
|
|
$this->processComment($doc,$method->getDocComment()); |
328
|
|
|
|
329
|
|
|
$params=array(); |
330
|
|
|
foreach($doc->input as $param) |
331
|
|
|
{ |
332
|
|
|
$type=empty($param->type)?'':$this->getTypeUrl($param->type).' '; |
333
|
|
|
if($param->isOptional) |
334
|
|
|
$params[]=$type.($param->isPassedByReference?'&':'').'$'.$param->name.'='.str_replace("\r",'',var_export($param->defaultValue,true)); |
335
|
|
|
else |
336
|
|
|
$params[]=$type.($param->isPassedByReference?'&':'').'$'.$param->name; |
337
|
|
|
} |
338
|
|
|
$doc->signature='{{'.$class->name.'::'.$doc->name.'|<b>'.$doc->name.'</b>}}('.implode(', ',$params).')'; |
339
|
|
|
if($doc->output!==null) |
340
|
|
|
$doc->signature=$this->getTypeUrl($doc->output->type).' '.$doc->signature; |
341
|
|
|
else |
342
|
|
|
$doc->signature='void '.$doc->signature; |
343
|
|
View Code Duplication |
if(($modifier=implode(' ',Reflection::getModifierNames($method->getModifiers())))!=='') |
|
|
|
|
344
|
|
|
$doc->signature=$modifier.' '.$doc->signature; |
345
|
|
|
|
346
|
|
|
return $doc; |
347
|
|
|
} |
348
|
|
|
|
349
|
|
View Code Duplication |
protected function getTypeUrl($type) |
|
|
|
|
350
|
|
|
{ |
351
|
|
|
if(isset($this->classes[$type]) && $type!==$this->_currentClass) |
352
|
|
|
return '{{'.$type.'|'.$type.'}}'; |
353
|
|
|
else |
354
|
|
|
return $type; |
355
|
|
|
} |
356
|
|
|
|
357
|
|
|
protected function processProperties($class) |
358
|
|
|
{ |
359
|
|
|
$properties=array(); |
360
|
|
|
foreach($class->getProperties() as $property) |
361
|
|
|
{ |
362
|
|
|
if($property->isPublic() || $property->isProtected()) |
363
|
|
|
{ |
364
|
|
|
$p=$this->processProperty($class,$property); |
365
|
|
|
$properties[$p->name]=$p; |
366
|
|
|
} |
367
|
|
|
} |
368
|
|
|
return $properties; |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
protected function processProperty($class,$property) |
372
|
|
|
{ |
373
|
|
|
$doc=new PropertyDoc; |
374
|
|
|
$doc->name=$property->getName(); |
375
|
|
|
$doc->definedBy=$property->getDeclaringClass()->getName(); |
376
|
|
|
$doc->readOnly=false; |
377
|
|
|
$doc->isStatic=$property->isStatic(); |
378
|
|
|
$doc->isProtected=$property->isProtected(); |
379
|
|
|
$doc->isInherited=$doc->definedBy!==$class->getName(); |
380
|
|
|
|
381
|
|
|
$this->processComment($doc,$property->getDocComment()); |
382
|
|
|
|
383
|
|
|
$doc->signature='<b>$'.$doc->name.'</b>;'; |
384
|
|
|
if($doc->type!==null) |
385
|
|
|
$doc->signature=$this->getTypeUrl($doc->type) . ' ' . $doc->signature; |
386
|
|
View Code Duplication |
if(($modifier=implode(' ',Reflection::getModifierNames($property->getModifiers())))!=='') |
|
|
|
|
387
|
|
|
$doc->signature=$modifier.' '.$doc->signature; |
388
|
|
|
|
389
|
|
|
return $doc; |
390
|
|
|
} |
391
|
|
|
|
392
|
|
View Code Duplication |
protected function processComponentProperties($class) |
|
|
|
|
393
|
|
|
{ |
394
|
|
|
$properties=array(); |
395
|
|
|
foreach($class->getMethods() as $method) |
396
|
|
|
{ |
397
|
|
|
if($this->isPropertyMethod($method) && ($method->isPublic() || $method->isProtected())) |
398
|
|
|
{ |
399
|
|
|
$p=$this->processComponentProperty($class,$method); |
400
|
|
|
$properties[$p->name]=$p; |
401
|
|
|
} |
402
|
|
|
} |
403
|
|
|
return $properties; |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
protected function processComponentProperty($class,$method) |
407
|
|
|
{ |
408
|
|
|
$doc=new PropertyDoc; |
409
|
|
|
$name=$method->getName(); |
410
|
|
|
$doc->name=strtolower($name[3]).substr($name,4); |
411
|
|
|
$doc->isProtected=$method->isProtected(); |
412
|
|
|
$doc->isStatic=false; |
413
|
|
|
$doc->readOnly=!$class->hasMethod('set'.substr($name,3)); |
414
|
|
|
$doc->definedBy=$method->getDeclaringClass()->getName(); |
415
|
|
|
$doc->isInherited=$doc->definedBy!==$class->getName(); |
416
|
|
|
$doc->getter=$this->processMethod($class,$method); |
417
|
|
|
if(!$doc->readOnly) |
418
|
|
|
$doc->setter=$this->processMethod($class,$class->getMethod('set'.substr($name,3))); |
419
|
|
|
|
420
|
|
|
$this->processComment($doc,$method->getDocComment()); |
421
|
|
|
|
422
|
|
|
return $doc; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
View Code Duplication |
protected function processComponentEvents($class) |
|
|
|
|
426
|
|
|
{ |
427
|
|
|
$events=array(); |
428
|
|
|
foreach($class->getMethods() as $method) |
429
|
|
|
{ |
430
|
|
|
if($this->isEventMethod($method) && ($method->isPublic() || $method->isProtected())) |
431
|
|
|
{ |
432
|
|
|
$e=$this->processComponentEvent($class,$method); |
433
|
|
|
$events[$e->name]=$e; |
434
|
|
|
} |
435
|
|
|
} |
436
|
|
|
return $events; |
437
|
|
|
} |
438
|
|
|
|
439
|
|
|
protected function processComponentEvent($class,$method) |
440
|
|
|
{ |
441
|
|
|
$doc=new EventDoc; |
442
|
|
|
$doc->name=$method->getName(); |
443
|
|
|
$doc->definedBy=$method->getDeclaringClass()->getName(); |
444
|
|
|
$doc->isInherited=$doc->definedBy!==$class->getName(); |
445
|
|
|
$doc->trigger=$this->processMethod($class,$method); |
446
|
|
|
|
447
|
|
|
$this->processComment($doc,$method->getDocComment()); |
448
|
|
|
|
449
|
|
|
return $doc; |
450
|
|
|
} |
451
|
|
|
|
452
|
|
|
protected function tagParam($object,$comment) |
453
|
|
|
{ |
454
|
|
|
if($object instanceof FunctionDoc) |
455
|
|
|
{ |
456
|
|
|
$param=current($object->input); |
457
|
|
|
if($param!==false) |
458
|
|
|
{ |
459
|
|
|
$segs=preg_split('/\s+/',$comment,2); |
460
|
|
|
$param->type=$segs[0]; |
461
|
|
|
if(preg_match('/\[\s*\]/',$param->type)) |
462
|
|
|
$param->type='array'; |
463
|
|
|
if(isset($segs[1])) |
464
|
|
|
{ |
465
|
|
|
/* |
466
|
|
|
* remove $variablename from description |
467
|
|
|
*/ |
468
|
|
|
$segs[1]=trim(preg_replace('/^\$\w+/','',$segs[1])); |
469
|
|
|
$param->description=$this->processDescription($segs[1]); |
470
|
|
View Code Duplication |
if(empty($object->introduction)) |
|
|
|
|
471
|
|
|
{ |
472
|
|
|
if(substr($object->name,0,3)=='set') |
473
|
|
|
$object->introduction='Sets '.$param->description; |
474
|
|
|
} |
475
|
|
|
} |
476
|
|
|
next($object->input); |
477
|
|
|
} |
478
|
|
|
} |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
protected function tagReturn($object,$comment) |
482
|
|
|
{ |
483
|
|
|
$segs=preg_split('/\s+/',$comment,2); |
484
|
|
|
if($object instanceof FunctionDoc) |
485
|
|
|
{ |
486
|
|
|
$object->output=new ParamDoc; |
487
|
|
|
$object->output->type=$segs[0]; |
488
|
|
|
if(isset($segs[1])) |
489
|
|
|
{ |
490
|
|
|
$object->output->description=$this->processDescription($segs[1]); |
491
|
|
|
if(empty($object->introduction)) |
492
|
|
|
{ |
493
|
|
|
/* |
494
|
|
|
* If no custom introduction, add automatically |
495
|
|
|
* with this getters introduction displayed in public methods table is resolved |
496
|
|
|
*/ |
497
|
|
|
if(substr($object->name,0,5)=='getIs') |
498
|
|
|
$object->introduction='Checks '.$object->output->description; |
499
|
|
View Code Duplication |
elseif(substr($object->name,0,3)=='get') |
|
|
|
|
500
|
|
|
$object->introduction='Returns '.$object->output->description; |
501
|
|
View Code Duplication |
elseif(substr($object->name,0,3)=='has') |
|
|
|
|
502
|
|
|
$object->introduction='Determines '.$object->output->description; |
503
|
|
|
} |
504
|
|
|
} |
505
|
|
|
} |
506
|
|
|
else if($object instanceof PropertyDoc) |
507
|
|
|
{ |
508
|
|
|
$object->type=$segs[0]; |
509
|
|
View Code Duplication |
if(isset($segs[1]) && empty($object->description)) |
|
|
|
|
510
|
|
|
{ |
511
|
|
|
if(($pos=strpos($segs[1],'.'))!==false) |
512
|
|
|
$object->introduction=$this->processDescription(substr($segs[1],0,$pos+1)); |
513
|
|
|
else |
514
|
|
|
$object->introduction=$this->processDescription($segs[1]); |
515
|
|
|
$object->description=$this->processDescription($segs[1]); |
516
|
|
|
} |
517
|
|
|
} |
518
|
|
|
} |
519
|
|
|
|
520
|
|
|
protected function tagVar($object,$comment) |
521
|
|
|
{ |
522
|
|
|
if($object instanceof PropertyDoc) |
523
|
|
|
{ |
524
|
|
|
$segs=preg_split('/\s+/',$comment,2); |
525
|
|
|
$object->type=$segs[0]; |
526
|
|
View Code Duplication |
if(isset($segs[1]) && empty($object->description)) |
|
|
|
|
527
|
|
|
{ |
528
|
|
|
if(($pos=strpos($segs[1],'.'))!==false) |
529
|
|
|
$object->introduction=$this->processDescription(substr($segs[1],0,$pos+1)); |
530
|
|
|
else |
531
|
|
|
$object->introduction=$this->processDescription($segs[1]); |
532
|
|
|
$object->description=$this->processDescription($segs[1]); |
533
|
|
|
} |
534
|
|
|
} |
535
|
|
|
} |
536
|
|
|
|
537
|
|
|
protected function tagSee($object,$comment) |
538
|
|
|
{ |
539
|
|
|
$segs=preg_split('/\s+/',trim($comment),2); |
540
|
|
|
$matches[1]=$segs[0]; |
|
|
|
|
541
|
|
|
$matches[2]=isset($segs[1])?$segs[1]:''; |
542
|
|
|
$object->see[]=$this->processLink($matches); |
543
|
|
|
} |
544
|
|
|
|
545
|
|
View Code Duplication |
protected function isPropertyMethod($method) |
|
|
|
|
546
|
|
|
{ |
547
|
|
|
$methodName=$method->getName(); |
548
|
|
|
return $method->getNumberOfRequiredParameters()===0 |
549
|
|
|
&& !$method->isStatic() |
550
|
|
|
&& strncasecmp($methodName,'get',3)===0 |
551
|
|
|
&& isset($methodName[3]); |
552
|
|
|
} |
553
|
|
|
|
554
|
|
View Code Duplication |
protected function isEventMethod($method) |
|
|
|
|
555
|
|
|
{ |
556
|
|
|
$methodName=$method->getName(); |
557
|
|
|
return strncasecmp($methodName,'on',2)===0 |
558
|
|
|
&& !$method->isStatic() |
559
|
|
|
&& isset($methodName[2]); |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
protected function getClassFiles($basePath) |
563
|
|
|
{ |
564
|
|
|
$files=array(); |
565
|
|
|
$folder=opendir($basePath); |
566
|
|
|
while($file=readdir($folder)) |
567
|
|
|
{ |
568
|
|
|
if($file==='.' || $file==='..') |
569
|
|
|
continue; |
570
|
|
|
$fullPath=realpath($basePath.DIRECTORY_SEPARATOR.$file); |
571
|
|
|
if($this->isValidPath($fullPath)) |
572
|
|
|
{ |
573
|
|
|
if(is_file($fullPath)) |
574
|
|
|
$files[]=$fullPath; |
575
|
|
|
else |
576
|
|
|
$files=array_merge($files,$this->getClassFiles($fullPath)); |
577
|
|
|
} |
578
|
|
|
} |
579
|
|
|
closedir($folder); |
580
|
|
|
return $files; |
581
|
|
|
} |
582
|
|
|
|
583
|
|
|
protected function isValidPath($path) |
584
|
|
|
{ |
585
|
|
|
if(is_file($path) && substr($path,-4)!=='.php') |
586
|
|
|
return false; |
587
|
|
|
$path=strtr($path,'\\','/'); |
588
|
|
|
foreach($this->_excludes as $exclude) |
|
|
|
|
589
|
|
|
{ |
590
|
|
|
if(($exclude[0]==='/' && $this->_sourcePath.$exclude===$path) || ($exclude[0]!=='/' && basename($path)===$exclude)) |
|
|
|
|
591
|
|
|
return false; |
592
|
|
|
} |
593
|
|
|
return true; |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
protected function findTargets() |
597
|
|
|
{ |
598
|
|
|
$oldClasses=get_declared_classes(); |
599
|
|
|
$oldInterfaces=get_declared_interfaces(); |
600
|
|
|
$oldFunctions=get_defined_functions(); |
601
|
|
|
$oldConstants=get_defined_constants(true); |
|
|
|
|
602
|
|
|
|
603
|
|
|
$classFiles=$this->getClassFiles($this->_sourcePath); |
604
|
|
|
require_once($this->_sourcePath.'/yii.php'); |
605
|
|
|
foreach($classFiles as $classFile) |
606
|
|
|
require_once($classFile); |
607
|
|
|
|
608
|
|
|
$classes=array_values(array_diff(get_declared_classes(),$oldClasses)); |
609
|
|
|
$interfaces=array_values(array_diff(get_declared_interfaces(),$oldInterfaces)); |
610
|
|
|
$classes=array_merge($classes,$interfaces); |
611
|
|
|
|
612
|
|
|
$n=count($classes); |
613
|
|
|
for($i=0;$i<$n;++$i) |
614
|
|
|
{ |
615
|
|
|
$class=new ReflectionClass($classes[$i]); |
616
|
|
|
$fileName=strtr($class->getFileName(),'\\','/'); |
617
|
|
|
foreach($this->_excludes as $exclude) |
618
|
|
|
{ |
619
|
|
|
if(($exclude[0]==='/' && strpos($fileName,$this->_sourcePath.$exclude)===0)) |
620
|
|
|
{ |
621
|
|
|
unset($classes[$i]); |
622
|
|
|
break; |
623
|
|
|
} |
624
|
|
|
} |
625
|
|
|
} |
626
|
|
|
|
627
|
|
|
sort($classes); |
628
|
|
|
$newFunctions=get_defined_functions(); |
629
|
|
|
$newConstants=get_defined_constants(true); |
630
|
|
|
$functions=array_values(array_diff($newFunctions['user'],$oldFunctions['user'])); |
631
|
|
|
$constants=$newConstants['user']; |
632
|
|
|
|
633
|
|
|
return array($classes,$functions,$constants); |
634
|
|
|
} |
635
|
|
|
|
636
|
|
|
/* |
637
|
|
|
* Checks @param directives in a source file |
638
|
|
|
* Detects: |
639
|
|
|
* missing @param directive (there is no @param directive for a function parameter) |
640
|
|
|
* missing function parameter (@param directive exists but that parameter is not in a function declaration) |
641
|
|
|
* missmatch parameters (if @param directive has different parameter name than a function - possible spelling error or wrong order of @param directives) |
642
|
|
|
*/ |
643
|
|
|
protected function checkSource($sourceFile) |
644
|
|
|
{ |
645
|
|
|
$fileContent=file($sourceFile); |
646
|
|
|
|
647
|
|
|
$docParam=array(); |
648
|
|
|
foreach($fileContent as $no=>$line) |
649
|
|
|
{ |
650
|
|
|
/* |
651
|
|
|
* Get lines with @param, and parameter name |
652
|
|
|
*/ |
653
|
|
|
if(preg_match('/^\s*\*\s*@param\s[A-Za-z0-9_\|\[\]]+\s(\$\w+)\s./',$line,$matches,PREG_OFFSET_CAPTURE)) |
654
|
|
|
{ |
655
|
|
|
$docParam[]=array( |
656
|
|
|
'docLine'=>$no+1, |
657
|
|
|
'docName'=>$matches[1][0], |
658
|
|
|
); |
659
|
|
|
continue; |
660
|
|
|
} |
661
|
|
|
/* |
662
|
|
|
* If function without parameters, there should be no parameters in $docParam |
663
|
|
|
*/ |
664
|
|
|
if(preg_match('/^\s*\w+[\s\w]*\sfunction\s\w+\(\s*\)/',$line,$matches,PREG_OFFSET_CAPTURE)) |
665
|
|
|
{ |
666
|
|
|
if(isset($docParam[0])) { |
667
|
|
|
$value=$docParam[0]; |
668
|
|
|
echo "ERROR.............: Parameter name not found!\n"; |
669
|
|
|
echo "Source file.......: ".$sourceFile."\n"; |
670
|
|
|
echo "PHPDoc line.......: ".$value['docLine']."\n"; |
671
|
|
|
echo "PHPDoc parameter..: ".$value['docName']."\n\n"; |
672
|
|
|
$docParam=array(); |
673
|
|
|
} |
674
|
|
|
continue; |
675
|
|
|
} |
676
|
|
|
/* |
677
|
|
|
* Get function variables in $matches[1][0] |
678
|
|
|
*/ |
679
|
|
|
if(preg_match('/^\s*\w+[\s\w]*\sfunction\s\w+\((.+)\)/',$line,$matches,PREG_OFFSET_CAPTURE)) |
680
|
|
|
{ |
681
|
|
|
$params=explode(",",$matches[1][0]); |
682
|
|
|
foreach($params as $br=>$param) |
683
|
|
|
{ |
684
|
|
|
/* |
685
|
|
|
* Strip anything that does not begin with $ (class types) eg. CHttpRequest $request |
686
|
|
|
*/ |
687
|
|
|
$param=preg_replace('/^\w+/','',trim($param)); |
688
|
|
|
/* |
689
|
|
|
* Strip default value if exists ex. data=array() (with spaces) |
690
|
|
|
*/ |
691
|
|
|
$param=preg_replace('/\s*=.+/','',trim($param)); |
692
|
|
|
/* |
693
|
|
|
* Strip & if pass by reference |
694
|
|
|
*/ |
695
|
|
|
if($param[0]=='&') |
696
|
|
|
$param=substr($param,1); |
697
|
|
|
/* |
698
|
|
|
* add parameter info to the docParam array |
699
|
|
|
*/ |
700
|
|
|
$docParam[$br]['parameterName']=$param; |
701
|
|
|
$docParam[$br]['parameterLine']=$no+1; |
702
|
|
|
} |
703
|
|
|
|
704
|
|
|
/* |
705
|
|
|
* All info gathered, let's make some checking |
706
|
|
|
*/ |
707
|
|
|
foreach($docParam as $value) |
708
|
|
|
{ |
709
|
|
View Code Duplication |
if(!isset($value['docLine']) || !isset($value['docName']) && isset($value['parameterName'])) |
|
|
|
|
710
|
|
|
{ |
711
|
|
|
echo "ERROR.............: Documentation not found!\n"; |
712
|
|
|
echo "Source file.......: ".$sourceFile."\n"; |
713
|
|
|
echo "Parameter line....: ".$value['parameterLine']."\n"; |
714
|
|
|
echo "Parameter name....: ".$value['parameterName']."\n\n"; |
715
|
|
|
} |
716
|
|
View Code Duplication |
if(!isset($value['parameterName']) || !isset($value['parameterLine'])) |
|
|
|
|
717
|
|
|
{ |
718
|
|
|
echo "ERROR.............: Parameter name not found!\n"; |
719
|
|
|
echo "Source file.......: ".$sourceFile."\n"; |
720
|
|
|
echo "PHPDoc line.......: ".$value['docLine']."\n"; |
721
|
|
|
echo "PHPDoc parameter..: ".$value['docName']."\n\n"; |
722
|
|
|
} |
723
|
|
|
if( isset($value['docName']) && isset($value['parameterName']) && $value['docName']!==$value['parameterName']) |
724
|
|
|
{ |
725
|
|
|
echo "ERROR.............: Wrong parameter order!\n"; |
726
|
|
|
echo "Source file.......: ".$sourceFile."\n"; |
727
|
|
|
echo "PHPDoc line.......: ".$value['docLine']."\n"; |
728
|
|
|
echo "PHPDoc parameter..: ".$value['docName']."\n"; |
729
|
|
|
echo "Parameter line....: ".$value['parameterLine']."\n"; |
730
|
|
|
echo "Parameter name....: ".$value['parameterName']."\n\n"; |
731
|
|
|
} |
732
|
|
|
} |
733
|
|
|
/* |
734
|
|
|
* reset $docParam |
735
|
|
|
*/ |
736
|
|
|
$docParam=array(); |
737
|
|
|
} |
738
|
|
|
} |
739
|
|
|
} |
740
|
|
|
} |
741
|
|
|
|
742
|
|
|
class BaseDoc |
743
|
|
|
{ |
744
|
|
|
public $name; |
745
|
|
|
public $since; |
746
|
|
|
public $see; |
747
|
|
|
public $introduction; |
748
|
|
|
public $description; |
749
|
|
|
|
750
|
|
|
public $sourcePath; |
751
|
|
|
public $startLine; |
752
|
|
|
public $endLine; |
753
|
|
|
|
754
|
|
|
public function loadSource($reflection) |
755
|
|
|
{ |
756
|
|
|
$this->sourcePath=str_replace('\\','/',str_replace(Yii::getPathOfAlias('basePath').DIRECTORY_SEPARATOR,'',$reflection->getFileName())); |
757
|
|
|
$this->startLine=$reflection->getStartLine(); |
758
|
|
|
$this->endLine=$reflection->getEndLine(); |
759
|
|
|
} |
760
|
|
|
|
761
|
|
|
public function getSourceUrl($baseUrl,$line=null) |
762
|
|
|
{ |
763
|
|
|
if($line===null) |
764
|
|
|
return $baseUrl.$this->sourcePath; |
765
|
|
|
else |
766
|
|
|
return $baseUrl.$this->sourcePath.'#'.$line; |
767
|
|
|
} |
768
|
|
|
|
769
|
|
|
public function getSourceCode() |
770
|
|
|
{ |
771
|
|
|
$lines=file($this->sourcePath); |
772
|
|
|
return implode("",array_slice($lines,$this->startLine-1,$this->endLine-$this->startLine+1)); |
773
|
|
|
} |
774
|
|
|
} |
775
|
|
|
|
776
|
|
|
class ClassDoc extends BaseDoc |
777
|
|
|
{ |
778
|
|
|
public $parentClasses=array(); |
779
|
|
|
public $subclasses=array(); |
780
|
|
|
public $interfaces=array(); |
781
|
|
|
public $isInterface; |
782
|
|
|
public $isAbstract; |
783
|
|
|
public $isFinal; |
784
|
|
|
|
785
|
|
|
public $signature; |
786
|
|
|
|
787
|
|
|
public $properties=array(); |
788
|
|
|
public $methods=array(); |
789
|
|
|
public $events=array(); |
790
|
|
|
public $constants=array(); |
791
|
|
|
|
792
|
|
|
public $protectedPropertyCount=0; |
793
|
|
|
public $publicPropertyCount=0; |
794
|
|
|
public $protectedMethodCount=0; |
795
|
|
|
public $publicMethodCount=0; |
796
|
|
|
|
797
|
|
|
public $nativePropertyCount=0; |
798
|
|
|
public $nativeMethodCount=0; |
799
|
|
|
public $nativeEventCount=0; |
800
|
|
|
|
801
|
|
|
public $package; |
802
|
|
|
public $version; |
803
|
|
|
} |
804
|
|
|
|
805
|
|
|
class PropertyDoc extends BaseDoc |
806
|
|
|
{ |
807
|
|
|
public $isProtected; |
808
|
|
|
public $isStatic; |
809
|
|
|
public $readOnly; |
810
|
|
|
public $isInherited; |
811
|
|
|
public $definedBy; |
812
|
|
|
|
813
|
|
|
public $type; |
814
|
|
|
public $signature; |
815
|
|
|
|
816
|
|
|
public $getter; |
817
|
|
|
public $setter; |
818
|
|
|
} |
819
|
|
|
|
820
|
|
|
class FunctionDoc extends BaseDoc |
821
|
|
|
{ |
822
|
|
|
public $signature; |
823
|
|
|
public $input=array(); |
824
|
|
|
public $output; |
825
|
|
|
} |
826
|
|
|
|
827
|
|
|
class MethodDoc extends FunctionDoc |
828
|
|
|
{ |
829
|
|
|
public $isAbstract; |
830
|
|
|
public $isFinal; |
831
|
|
|
public $isProtected; |
832
|
|
|
public $isStatic; |
833
|
|
|
public $isInherited; |
834
|
|
|
public $definedBy; |
835
|
|
|
} |
836
|
|
|
|
837
|
|
|
class EventDoc extends BaseDoc |
838
|
|
|
{ |
839
|
|
|
public $isInherited; |
840
|
|
|
public $definedBy; |
841
|
|
|
public $trigger; |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
class ParamDoc |
845
|
|
|
{ |
846
|
|
|
public $name; |
847
|
|
|
public $description; |
848
|
|
|
public $type; |
849
|
|
|
public $isOptional; |
850
|
|
|
public $defaultValue; |
851
|
|
|
public $isPassedByReference; |
852
|
|
|
} |
853
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.