055d7295dd5bb886883b0808408a5f0af71c27ae
[atutor.git] / mods / phpdoc / PHPDoc / parser / PhpdocParser.php
1 <?php\r
2 /**\r
3 * "Main" class of the Parser collection.\r
4\r
5 * Note that a lot of communication is done using shared instance variables.\r
6\r
7 * @version      $Id: PhpdocParser.php,v 1.2 2000/12/03 22:37:37 uw Exp $\r
8 */\r
9 class PhpdocParser extends PhpdocClassParser {\r
10 \r
11         /**\r
12         * Name of the file currently parsed. \r
13         * \r
14         * Instead of passing the name of the current file by argument\r
15         * PHPDoc uses this slot to communicate. Yeah I know, it's\r
16         * the way methods should communicate, but it saves me a lot \r
17         * a lot of work.\r
18         * @var  string  Name of the file currently parsed.\r
19         */\r
20         var $currentFile = "";\r
21         \r
22         /**\r
23         * Array of PHP Sourcecode Files to examine.\r
24         *\r
25         * The array keys hold the filenames, the array values the file content.\r
26         *\r
27         * @var  array           \r
28         * @see  parse()\r
29         */                                                                                                      \r
30         var     $phpfiles = array();                                                                                                    \r
31         \r
32         /**\r
33         * Mapping from classnames to filenames\r
34         *\r
35         * @var  array\r
36         */\r
37         var $classnamesToFilenames = array();\r
38         \r
39         /**\r
40         * Hash with the data of the current class tree (one parentclass with all children).\r
41         *\r
42         * @var  array\r
43         * @see  $modules\r
44         */\r
45         var $classes = array();\r
46         \r
47         /**\r
48         * List of all parentclasses found.\r
49         * @var  array\r
50         */\r
51         var $baseclasses = array();\r
52 \r
53         /**\r
54         * List of all files containing classes.\r
55         *\r
56         * @var array\r
57         */              \r
58         var $classfiles = array();\r
59         \r
60         /**\r
61         * Hash of all class trees. \r
62         *\r
63         * @var array\r
64         */\r
65         var $classtree = array();\r
66 \r
67         /**\r
68         * List of all files containing modules.\r
69         *\r
70         * @var  array\r
71         */      \r
72         var $modulefiles = array();\r
73         \r
74         /**\r
75         * List of all module groups.\r
76         *\r
77         * @var array\r
78         */\r
79         var $modulegroups = array();\r
80         \r
81         /**\r
82         * Hash with the data of the current module group.\r
83         *\r
84         * @var  array\r
85         * @see  $classes\r
86         */\r
87         var $modules = array();\r
88 \r
89         /**\r
90         * Hash of all packages found.\r
91         *\r
92         * @var  array\r
93         */      \r
94         var $packages = array();\r
95 \r
96         /**\r
97         * Flag indicating that getClassTree() was called.\r
98         *\r
99         * @var  boolean \r
100         * @see  getClassTree()\r
101         */\r
102         var $flag_classtree = false;\r
103         \r
104         /**\r
105         * Flag indicating that getModulegroup was called.\r
106         *\r
107         * @var  boolean\r
108         * @see  getModulegroup()\r
109         */\r
110         var $flag_modulegroup = false;  \r
111         \r
112         /**\r
113         * Name of the base class of the current class tree.\r
114         *\r
115         * @var  string\r
116         * @see  getClassTree()\r
117         */\r
118         var $current_baseclass = "";\r
119         \r
120         /**\r
121         * Creates an instance of PhpdocWarning and calls buildComplexRegExps() to initialize the object.\r
122         *\r
123         * @param        boolean  If true the parser prints status messages.\r
124         * @see  $warn, buildComplexRegExps()\r
125         */\r
126         function PhpdocParser($flag_output = false) {\r
127         \r
128                 if ($flag_output)\r
129                         $this->setFlagOutput(true);\r
130                 else \r
131                         $this->setFlagOutput(false);\r
132                         \r
133                 $this->buildComplexRegExps();\r
134                 \r
135         } // end constructor\r
136         \r
137         /**\r
138         * Central parsing function.\r
139         *\r
140         * With version 0.3alpha PHPdoc changed the way the parser works. It does now\r
141         * 1 1/2 parsing runs. One prescan to build the class trees and a list of module\r
142         * groups and one deep scan to extract the information. This reduces the memory \r
143         * consumption.\r
144         * \r
145         * @return       boolean $ok\r
146         * @access       public  \r
147         * @see  findModulegroups(), findClassTrees(), getModulesAndClasses()\r
148         */\r
149         function preparse() {\r
150 \r
151                 if (0 == count($this->phpfiles)) {\r
152                         $this->err[] = new PHPDocError("Can't parse - no files defined.", __FILE__, __LINE__);\r
153                         return false;\r
154                 }\r
155                 \r
156                 $para = array();\r
157                 reset($this->phpfiles);\r
158                 while (list($filename, $phpcode) = each($this->phpfiles))\r
159                         $para[$filename] = $this->getModulesAndClasses($phpcode);\r
160                         \r
161                 $this->findModulegroups($para);\r
162                 $this->findClassTrees($para);           \r
163                 \r
164                 return true;            \r
165         } // end func preparse\r
166         \r
167         /**\r
168         * Returns the data of one parentclass and all it's subclasses or false.\r
169         *\r
170         * Use this function to loop through the class trees. The loop should look somewhat like: \r
171         * <code>while ( $classtree = $parser->getClassTree() ) ...</code>\r
172         *  \r
173         * @return mixed         $classes        Hash with the data of the current class tree or false.\r
174         * @access       public\r
175         * @see          getModulegroup(), $baseclasses\r
176         */\r
177         function getClassTree() {\r
178         \r
179                 // first call, reset the baseclass array pointer\r
180                 if (!$this->flag_classtree) {\r
181                         reset($this->baseclasses);\r
182                         $this->flag_classtree = true;\r
183                 }\r
184                 \r
185                 if (list($classname, $filename) = each($this->baseclasses)) {\r
186                 \r
187                         $this->classes = array();\r
188                         $this->current_baseclass = $classname;\r
189                         \r
190                         $this->addClass($classname, $filename);\r
191 \r
192                         return $this->classes;\r
193                         \r
194                 } else {\r
195                 \r
196                         return false;\r
197                         \r
198                 }\r
199                 \r
200         } // end func getClassTree\r
201         \r
202         /**\r
203         * Returns the data of one module group.\r
204         * \r
205         * Use this function to loop through the module groups. The loop should look somewhat like:\r
206         * <code>while ( $modulegroup = $parser->getModulegroup() ) ...</code>.\r
207         *\r
208         * @return       mixed           $modulegroup    Hash with the data of the current class tree or false.\r
209         * @access       public\r
210         * @see          getClassTree(), addModule(), $modulegroups\r
211         */\r
212         function getModulegroup() {\r
213                 \r
214                 if (!$this->flag_modulegroup) {\r
215                         reset($this->modulegroups);\r
216                         $this->flag_modulegroup = true;\r
217                 }\r
218                 \r
219                 if (list($group, $modules) = each($this->modulegroups)) {\r
220                         \r
221                         $this->modules = array();\r
222                         while (list($modulename, $files) = each($modules)) {\r
223                                 reset($files);\r
224                                 while (list($k, $filename) = each($files))\r
225                                         $this->addModule($group, $filename);            \r
226                         }\r
227                         \r
228                         return $this->modules;\r
229                         \r
230                 } else {\r
231                 \r
232                         return false;\r
233                 \r
234                 }\r
235                 \r
236         } // end func getModulegroup\r
237         \r
238         /**\r
239         *       Analyses the given file and adds the result to the module list.\r
240         * \r
241         * The function analyses the given file, unsets the file in the \r
242         * file list, adds the result of the parser to the module list and \r
243         * if necessary it adds some data to the package list.\r
244         *\r
245         * @param        string  Name of the module group the parsing result gets added.\r
246         * @param        string  Name of the file to scan.\r
247         * @see  getPhpdocParagraphs(), analyseModule()\r
248         */      \r
249         function addModule($group, $filename) {\r
250 \r
251                 $data = $this->getPhpdocParagraphs($this->phpfiles[$filename], array("classes", "variables") ); \r
252                 // free memory as soon as possible...\r
253                 unset($this->phpfiles[$filename]);\r
254                 \r
255                 // note: not passed by argument\r
256                 $this->currentFile = $filename;\r
257                 $result = $this->analyseModule($data);\r
258                 $result["filename"] = $filename;\r
259                 \r
260                 $this->modules[$group][$result["name"]] = $result;\r
261                                         \r
262                 if (isset($result["package"]))\r
263                         $this->packages[$result["package"]]["modules"][] = array (\r
264                                                                                                                                                                                                                                                                 "name"                  => $result["name"],\r
265                                                                                                                                                                                                                                                                 "group"                 => $result["group"],\r
266                                                                                                                                                                                                                                                                 "filename"      => $filename\r
267                                                                                                                                                                                                                                                         );\r
268                 \r
269         } // end func addModule\r
270         \r
271         /**\r
272         * Analyses the given file and adds the result to the class list.\r
273         * \r
274         * The first parameter (classname) comes from the prescan done \r
275         * by findClassTrees()\r
276         *\r
277         * @param        string  Name of the class that gets added.\r
278         * @param        string  Name of the file to scan.\r
279         * @see  addSubclasses(), analyseClass(), $classes\r
280         */\r
281         function addClass($classname, $filename) {\r
282                 \r
283                 $data = $this->getPhpdocParagraphs($this->phpfiles[$filename], array("modules") );\r
284                 // free memory as soon as possible...\r
285                 unset($this->phpfiles[$filename]);\r
286                 \r
287                 $this->currentFile = $filename;\r
288                 $result = $this->analyseClass($data);\r
289 \r
290                 // Add some informations from the classtree that was build by the prescan to the class.\r
291                 $fields = array("subclasses", "noparent", "path", "baseclass");         \r
292                 reset($fields);\r
293                 while (list($k, $field) = each($fields))\r
294                         if (isset($this->classtree[$filename][$classname][$field]))\r
295                                 $result[$field] = $this->classtree[$filename][$classname][$field];\r
296 \r
297                 $result["filename"] = $filename;                \r
298                 \r
299                 $this->classes[$classname] = $result;           \r
300                 $this->addSubclasses($classname);\r
301                 \r
302                 if (isset($result["package"]))\r
303                         $this->packages[$result["package"]]["classes"][] = $classname;\r
304                                 \r
305         } // end func addClass\r
306         \r
307         /**\r
308         * Adds recursively subclasses to the specified class.\r
309         *\r
310         * @param        string Name of the class that might contain subclasses\r
311         * @see  addClass()\r
312         */\r
313         function addSubclasses($classname) {\r
314                 \r
315                 if (isset($this->classes[$classname]["subclasses"])) {\r
316                         \r
317                         $subclasses = $this->classes[$classname]["subclasses"];\r
318                         while (list($subclass, $v) = each($subclasses)) \r
319                                 $this->addClass($subclass, $this->classnamesToFilenames[$subclass]);\r
320                                 \r
321                 }\r
322                 \r
323         } // end func addSubclasses\r
324         \r
325         /**\r
326         * Builds the hash of module groups and the module file list.\r
327         *\r
328         * @param        array   Hash with the result of getClassesAndModules() of all files\r
329         * @see  parse(), findClassTree(), $modulegroups, $modulefiles\r
330         */\r
331         function findModulegroups($para) {\r
332                 \r
333                 reset($para);\r
334                 while (list($filename, $data) = each($para)) {\r
335 \r
336                         if (isset($data["modules"]["name"])) {\r
337                         \r
338                                 $name = ("" != $data["modules"]["name"]) ? $data["modules"]["name"] : $filename;\r
339                                 $group = ("" != $data["modules"]["group"]) ? $data["modules"]["group"] : $name;                 \r
340                                 \r
341                                 if (0 != count($data["classes"])) {\r
342                                         // As we do not have a real parser that returns a parsing tree we can't \r
343                                         // handle modules and classes in one file. Drop a note to the user.\r
344                                         $this->warn->addDocWarning(     $filename, "module", $name, "PHPDoc is confused: module files must not contain classes. Doc will probably be broken, module gets ignored.", "collision" );\r
345                                         continue;\r
346                                 }\r
347 \r
348                                 if (isset($this->modulegroups[$group][$name])) \r
349                                         $this->warn->addDocWarning($filename, "module", $name, "Warning: there's more than one module '$name' (file: '$filename) in the module group '$group'.", "warning");\r
350 \r
351                                 $this->modulegroups[$group][$name][] = $filename;                                       \r
352                                 $this->modulefiles[] = $filename;                               \r
353                                                                 \r
354                         }\r
355                         \r
356                 }\r
357 \r
358         } // end func findModulegroups\r
359         \r
360         /**\r
361         * Builds a hash of all class trees.\r
362         *\r
363         * @param array  Hash with the result of getClassesAndModules() of all files\r
364         * @see  parse(), findModulegroups(), $classnamesToFilenames, $classtree, $classfiles, $baseclasses\r
365         */\r
366         function findClassTrees($para) {\r
367                 \r
368                 reset($para);\r
369                 while(list($filename, $data) = each($para)) {\r
370                 \r
371                         if (0!=count($data["classes"])) {\r
372 \r
373                                 $classname = $data["classes"][0]["name"];\r
374                                                                                                                         \r
375                                 if (1<count($data["classes"]))\r
376                                         $this->warn->addDocWarning($filename, "class", $classname , "PHPDoc is confused: there is more than one class in this file. Doc will probably be broken, first class '$classname' gets used, file '$filename' get ignored.", "collision");\r
377                                         \r
378                                 if (isset($data["modules"]["name"]))\r
379                                         $this->warn->addDocWarning($filename, "class", "", "Warning: found a module comment in a class file. Module comment gets ignored, doc might be broken.", "collision");\r
380                                 \r
381                                 $this->classnamesToFilenames[$classname] = $filename;\r
382                                 $this->classtree[$filename][$classname] = $data["classes"][0];\r
383                                 $this->classfiles[] = $filename;\r
384                                                                                                                                 \r
385                         }\r
386                         \r
387                 }\r
388                 \r
389                 reset($this->classnamesToFilenames);\r
390                 while (list($classname, $filename)=each($this->classnamesToFilenames)) {\r
391 \r
392                         $path                   = array();\r
393                         $baseclass      = $classname;\r
394                         $basefile       = $filename;\r
395                         $flag_noparent  = false;\r
396                         \r
397                         while ($extends = $this->classtree[$basefile][$baseclass]["extends"]) {\r
398                                 if (!isset($this->classnamesToFilenames[$extends])) {\r
399                                         $flag_noparent = true;\r
400                                         break;\r
401                                 }\r
402 \r
403                                 $this->classtree[$this->classnamesToFilenames[$extends]][$extends]["subclasses"][$baseclass] = true;\r
404                                 $path[]                 = $extends;\r
405                                 $baseclass      = $extends;\r
406                                 $basefile       = $this->classnamesToFilenames[$baseclass];\r
407                         }\r
408                         \r
409                         if ($flag_noparent)\r
410                                 $this->classtree[$filename][$classname]["noparent"] = $flag_noparent;\r
411                         \r
412                         $base = (0 == count($path)) ? true : false;\r
413                         if ($base) \r
414                                 $this->baseclasses[$classname] = $filename;\r
415                         else \r
416                                 $this->classtree[$filename][$classname]["path"] = $path;\r
417                                 \r
418                         if ($baseclass != $classname)\r
419                                 $this->classtree[$filename][$classname]["baseclass"] = $baseclass;\r
420                 }\r
421                 \r
422         } // end func findClassTrees\r
423 \r
424         /**\r
425         * Returns the mapping array from classnames to filenames\r
426         *\r
427         * @return array \r
428         * @see          $classnamesToFilenames\r
429         */\r
430         function getClassnamesToFilenames() {\r
431                 return $this->classnamesToFilenames;\r
432         } // end func getClassnamesToFilenames\r
433 \r
434         \r
435         /**\r
436         * Sets the list of PHP Soucecode Files to examine.\r
437         * @param        array           $phpfiles\r
438         * @return       bool            $ok\r
439         * @access       public\r
440         */\r
441         function setPhpSourcecodeFiles($phpfiles) {\r
442                 if (!is_array($phpfiles) || 0 == count($phpfiles)) \r
443                         return false;\r
444                 \r
445                 $this->phpfiles = $phpfiles;\r
446                 return true;    \r
447         } // end func setPhpSourcecodeFiles\r
448         \r
449 } // end class PhpdocParser\r
450 ?>