5 * Documentation generator
7 * This scripts scans all files in the lib/ directory, and generates
8 * Google Code wiki documentation.
10 * This script is rather crappy. It does what it needs to do, but uses global
11 * variables and it might be a hard to read.
13 * I'm not sure if I care though. Maybe one day this can become a separate
16 * To run this script, just execute on the command line. The script assumes
17 * it's in the standard bin/ directory.
19 date_default_timezone_set('UTC');
21 $libDir = realpath(__DIR__ . '/../lib');
22 $outputDir = __DIR__ . '/../docs/wikidocs';
24 if (!is_dir($outputDir)) mkdir($outputDir);
26 $files = new RecursiveDirectoryIterator($libDir);
27 $files = new RecursiveIteratorIterator($files, RecursiveIteratorIterator::LEAVES_ONLY);
29 include_once $libDir . '/Sabre/autoload.php';
31 // Finding all classnames
32 $classNames = findClassNames($files);
33 echo "Found: " . count($classNames) . " classes and interfaces\n";
35 echo "Generating class tree\n";
36 $classTree = getClassTree($classNames);
38 $packageList = array();
40 foreach($classNames as $className) {
42 echo "Creating docs for: " . $className . "\n";
44 $output = createDoc($className,isset($classTree[$className])?$classTree[$className]:array());
45 file_put_contents($outputDir . '/' . $className . '.wiki', $output);
49 echo "Creating indexes\n";
50 $output = createSidebarIndex($packageList);
51 file_put_contents($outputDir . '/APIIndex.wiki', $output);
54 function findClassNames($files) {
56 $classNames = array();
57 foreach($files as $fileName=>$fileInfo) {
59 $tokens = token_get_all(file_get_contents($fileName));
60 foreach($tokens as $tokenIndex=>$token) {
62 if ($token[0]===T_CLASS || $token[0]===T_INTERFACE) {
63 $classNames[] = $tokens[$tokenIndex+2][1];
74 function getClassTree($classNames) {
78 foreach($classNames as $className) {
80 if (!class_exists($className) && !interface_exists($className)) continue;
81 $rClass = new ReflectionClass($className);
83 $parent = $rClass->getParentClass();
84 if ($parent) $parent = $parent->name;
86 if (!isset($classTree[$parent])) $classTree[$parent] = array();
87 $classTree[$parent][] = $className;
89 foreach($rClass->getInterfaceNames() as $interface) {
91 if (!isset($classTree[$interface])) {
92 $classTree[$interface] = array();
94 $classTree[$interface][] = $className;
103 function createDoc($className, $extendedBy) {
109 $rClass = new ReflectionClass($className);
111 echo "#summary API documentation for: ", $rClass->getName() , "\n";
112 echo "#labels APIDoc\n";
113 echo "#sidebar APIIndex\n";
114 echo "=`" . $rClass->getName() . "`=\n";
117 $docs = parseDocs($rClass->getDocComment());
118 echo $docs['description'] . "\n";
121 $parentClass = $rClass->getParentClass();
124 echo " * Parent class: [" . $parentClass->getName() . "]\n";
126 if ($interfaces = $rClass->getInterfaceNames()) {
127 $interfaces = array_map(function($int) { return '[' . $int . ']'; },$interfaces);
128 echo " * Implements: " . implode(", ", $interfaces) . "\n";
130 $classType = $rClass->isInterface()?'interface':'class';
131 if (isset($docs['deprecated'])) {
132 echo " * *Warning: This $classType is deprecated, and should not longer be used.*\n";
134 if ($rClass->isInterface()) {
135 echo " * This is an interface.\n";
136 } elseif ($rClass->isAbstract()) {
137 echo " * This is an abstract class.\n";
139 if (isset($docs['package'])) {
140 $package = $docs['package'];
141 if (isset($docs['subpackage'])) {
142 $package.='_' . $docs['subpackage'];
144 if (!isset($packageList[$package])) {
145 $packageList[$package] = array();
147 $packageList[$package][] = $rClass->getName();
153 if ($classType==='interface') {
154 echo "This interface is extended by the following interfaces:\n";
155 foreach($extendedBy as $className) {
156 if (interface_exists($className)) {
157 echo " * [" . $className . "]\n";
161 echo "This interface is implemented by the following classes:\n";
163 echo "This class is extended by the following classes:\n";
165 foreach($extendedBy as $className) {
166 if (class_exists($className)) {
167 echo " * [" . $className . "]\n";
175 echo "==Properties==\n";
179 $properties = $rClass->getProperties(ReflectionProperty::IS_STATIC | ReflectionProperty::IS_PUBLIC | ReflectionProperty::IS_PROTECTED);
181 if (count($properties)>0) {
182 foreach($properties as $rProperty) {
184 createPropertyDoc($rProperty);
188 echo "This $classType does not define any public or protected properties.\n";
193 echo "==Methods==\n";
197 $methods = $rClass->getMethods(ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED);
199 if (count($methods)>0) {
200 foreach($methods as $rMethod) {
202 createMethodDoc($rMethod, $rClass);
206 echo "\nThis $classType does not define any public or protected methods.\n";
209 return ob_get_clean();
213 function createMethodDoc($rMethod, $rClass) {
215 echo "===`" . $rMethod->getName() . "`===\n";
218 $docs = parseDocs($rMethod->getDocComment());
220 $return = isset($docs['return'])?$docs['return']:'void';
223 echo $return . " " . $rMethod->class . "::" . $rMethod->getName() . "(";
224 foreach($rMethod->getParameters() as $parameter) {
225 if ($parameter->getPosition()>0) echo ", ";
226 if ($class = $parameter->getClass()) {
227 echo $class->name . " ";
228 } elseif (isset($docs['param'][$parameter->name])) {
229 echo $docs['param'][$parameter->name] . " ";
232 echo '$' . $parameter->name;
234 if ($parameter->isOptional() && $parameter->isDefaultValueAvailable()) {
235 $default = $parameter->getDefaultValue();
236 $default = var_export($default,true);
237 $default = str_replace("\n","",$default);
238 echo " = " . $default;
246 echo $docs['description'] . "\n";
251 if (isset($docs['deprecated'])) {
252 echo " * *Warning: This method is deprecated, and should not longer be used.*\n";
255 if ($rMethod->isProtected()) {
256 echo " * This method is protected.\n";
259 if ($rMethod->isPrivate()) {
260 echo " * This method is private.\n";
263 if ($rMethod->isAbstract()) {
264 echo " * This is an abstract method\n";
268 if ($rMethod->class != $rClass->name) {
269 echo " * Defined in [" . $rMethod->class . "]\n";
273 if ($hasProp) echo "\n";
277 function createPropertyDoc($rProperty) {
279 echo "===`" . $rProperty->getName() . "`===\n";
282 $docs = parseDocs($rProperty->getDocComment());
284 $visibility = 'public';
285 if ($rProperty->isProtected()) $visibility = 'protected';
286 if ($rProperty->isPrivate()) $visibility = 'private';
289 echo $visibility . " " . $rProperty->class . "::$" . $rProperty->getName();
293 echo $docs['description'] . "\n";
298 if (isset($docs['deprecated'])) {
299 echo " * *Warning: This property is deprecated, and should not longer be used.*\n";
302 if ($rProperty->isProtected()) {
303 echo " * This property is protected.\n";
306 if ($rProperty->isPrivate()) {
307 echo " * This property is private.\n";
310 if ($rProperty->isStatic()) {
311 echo " * This property is static.\n";
315 if ($hasProp) echo "\n";
319 function parseDocs($docString) {
322 $description = array();
324 // Trimming all the comment characters
325 $docString = trim($docString,"\n*/ ");
326 $docString = explode("\n",$docString);
328 foreach($docString as $str) {
330 $str = ltrim($str,'* ');
332 if ($str && $str[0]==='@') {
333 $r = explode(' ',substr($str,1),2);
335 $paramValue = (count($r)>1)?$r[1]:'';
337 // 'param' paramName is special. Confusing, I know.
338 if ($paramName==='param') {
339 if (!isset($params['param'])) $params['param'] = array();
340 $paramValue = explode(' ', $paramValue,3);
341 $params['param'][substr($paramValue[1],1)] = $paramValue[0];
343 $params[$paramName] = trim($paramValue);
351 $params['description'] = trim(implode("\n",$description),"\n ");
357 function createSidebarIndex($packageList) {
360 echo "#labels APIDocs\n";
361 echo "#summary List of all classes, neatly organized\n";
362 echo "=API Index=\n";
364 foreach($packageList as $package=>$classes) {
366 echo " * $package\n";
368 foreach($classes as $class) {
370 echo " * [$class $class]\n";
376 return ob_get_clean();