3 * "Main" class of the Parser collection.
\r
5 * Note that a lot of communication is done using shared instance variables.
\r
7 * @version $Id: PhpdocParser.php,v 1.2 2000/12/03 22:37:37 uw Exp $
\r
9 class PhpdocParser extends PhpdocClassParser {
\r
12 * Name of the file currently parsed.
\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
18 * @var string Name of the file currently parsed.
\r
20 var $currentFile = "";
\r
23 * Array of PHP Sourcecode Files to examine.
\r
25 * The array keys hold the filenames, the array values the file content.
\r
30 var $phpfiles = array();
\r
33 * Mapping from classnames to filenames
\r
37 var $classnamesToFilenames = array();
\r
40 * Hash with the data of the current class tree (one parentclass with all children).
\r
45 var $classes = array();
\r
48 * List of all parentclasses found.
\r
51 var $baseclasses = array();
\r
54 * List of all files containing classes.
\r
58 var $classfiles = array();
\r
61 * Hash of all class trees.
\r
65 var $classtree = array();
\r
68 * List of all files containing modules.
\r
72 var $modulefiles = array();
\r
75 * List of all module groups.
\r
79 var $modulegroups = array();
\r
82 * Hash with the data of the current module group.
\r
87 var $modules = array();
\r
90 * Hash of all packages found.
\r
94 var $packages = array();
\r
97 * Flag indicating that getClassTree() was called.
\r
100 * @see getClassTree()
\r
102 var $flag_classtree = false;
\r
105 * Flag indicating that getModulegroup was called.
\r
108 * @see getModulegroup()
\r
110 var $flag_modulegroup = false;
\r
113 * Name of the base class of the current class tree.
\r
116 * @see getClassTree()
\r
118 var $current_baseclass = "";
\r
121 * Creates an instance of PhpdocWarning and calls buildComplexRegExps() to initialize the object.
\r
123 * @param boolean If true the parser prints status messages.
\r
124 * @see $warn, buildComplexRegExps()
\r
126 function PhpdocParser($flag_output = false) {
\r
129 $this->setFlagOutput(true);
\r
131 $this->setFlagOutput(false);
\r
133 $this->buildComplexRegExps();
\r
135 } // end constructor
\r
138 * Central parsing function.
\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
145 * @return boolean $ok
\r
147 * @see findModulegroups(), findClassTrees(), getModulesAndClasses()
\r
149 function preparse() {
\r
151 if (0 == count($this->phpfiles)) {
\r
152 $this->err[] = new PHPDocError("Can't parse - no files defined.", __FILE__, __LINE__);
\r
157 reset($this->phpfiles);
\r
158 while (list($filename, $phpcode) = each($this->phpfiles))
\r
159 $para[$filename] = $this->getModulesAndClasses($phpcode);
\r
161 $this->findModulegroups($para);
\r
162 $this->findClassTrees($para);
\r
165 } // end func preparse
\r
168 * Returns the data of one parentclass and all it's subclasses or false.
\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
173 * @return mixed $classes Hash with the data of the current class tree or false.
\r
175 * @see getModulegroup(), $baseclasses
\r
177 function getClassTree() {
\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
185 if (list($classname, $filename) = each($this->baseclasses)) {
\r
187 $this->classes = array();
\r
188 $this->current_baseclass = $classname;
\r
190 $this->addClass($classname, $filename);
\r
192 return $this->classes;
\r
200 } // end func getClassTree
\r
203 * Returns the data of one module group.
\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
208 * @return mixed $modulegroup Hash with the data of the current class tree or false.
\r
210 * @see getClassTree(), addModule(), $modulegroups
\r
212 function getModulegroup() {
\r
214 if (!$this->flag_modulegroup) {
\r
215 reset($this->modulegroups);
\r
216 $this->flag_modulegroup = true;
\r
219 if (list($group, $modules) = each($this->modulegroups)) {
\r
221 $this->modules = array();
\r
222 while (list($modulename, $files) = each($modules)) {
\r
224 while (list($k, $filename) = each($files))
\r
225 $this->addModule($group, $filename);
\r
228 return $this->modules;
\r
236 } // end func getModulegroup
\r
239 * Analyses the given file and adds the result to the module list.
\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
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
249 function addModule($group, $filename) {
\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
255 // note: not passed by argument
\r
256 $this->currentFile = $filename;
\r
257 $result = $this->analyseModule($data);
\r
258 $result["filename"] = $filename;
\r
260 $this->modules[$group][$result["name"]] = $result;
\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
269 } // end func addModule
\r
272 * Analyses the given file and adds the result to the class list.
\r
274 * The first parameter (classname) comes from the prescan done
\r
275 * by findClassTrees()
\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
281 function addClass($classname, $filename) {
\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
287 $this->currentFile = $filename;
\r
288 $result = $this->analyseClass($data);
\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
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
297 $result["filename"] = $filename;
\r
299 $this->classes[$classname] = $result;
\r
300 $this->addSubclasses($classname);
\r
302 if (isset($result["package"]))
\r
303 $this->packages[$result["package"]]["classes"][] = $classname;
\r
305 } // end func addClass
\r
308 * Adds recursively subclasses to the specified class.
\r
310 * @param string Name of the class that might contain subclasses
\r
313 function addSubclasses($classname) {
\r
315 if (isset($this->classes[$classname]["subclasses"])) {
\r
317 $subclasses = $this->classes[$classname]["subclasses"];
\r
318 while (list($subclass, $v) = each($subclasses))
\r
319 $this->addClass($subclass, $this->classnamesToFilenames[$subclass]);
\r
323 } // end func addSubclasses
\r
326 * Builds the hash of module groups and the module file list.
\r
328 * @param array Hash with the result of getClassesAndModules() of all files
\r
329 * @see parse(), findClassTree(), $modulegroups, $modulefiles
\r
331 function findModulegroups($para) {
\r
334 while (list($filename, $data) = each($para)) {
\r
336 if (isset($data["modules"]["name"])) {
\r
338 $name = ("" != $data["modules"]["name"]) ? $data["modules"]["name"] : $filename;
\r
339 $group = ("" != $data["modules"]["group"]) ? $data["modules"]["group"] : $name;
\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
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
351 $this->modulegroups[$group][$name][] = $filename;
\r
352 $this->modulefiles[] = $filename;
\r
358 } // end func findModulegroups
\r
361 * Builds a hash of all class trees.
\r
363 * @param array Hash with the result of getClassesAndModules() of all files
\r
364 * @see parse(), findModulegroups(), $classnamesToFilenames, $classtree, $classfiles, $baseclasses
\r
366 function findClassTrees($para) {
\r
369 while(list($filename, $data) = each($para)) {
\r
371 if (0!=count($data["classes"])) {
\r
373 $classname = $data["classes"][0]["name"];
\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
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
381 $this->classnamesToFilenames[$classname] = $filename;
\r
382 $this->classtree[$filename][$classname] = $data["classes"][0];
\r
383 $this->classfiles[] = $filename;
\r
389 reset($this->classnamesToFilenames);
\r
390 while (list($classname, $filename)=each($this->classnamesToFilenames)) {
\r
393 $baseclass = $classname;
\r
394 $basefile = $filename;
\r
395 $flag_noparent = false;
\r
397 while ($extends = $this->classtree[$basefile][$baseclass]["extends"]) {
\r
398 if (!isset($this->classnamesToFilenames[$extends])) {
\r
399 $flag_noparent = true;
\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
409 if ($flag_noparent)
\r
410 $this->classtree[$filename][$classname]["noparent"] = $flag_noparent;
\r
412 $base = (0 == count($path)) ? true : false;
\r
414 $this->baseclasses[$classname] = $filename;
\r
416 $this->classtree[$filename][$classname]["path"] = $path;
\r
418 if ($baseclass != $classname)
\r
419 $this->classtree[$filename][$classname]["baseclass"] = $baseclass;
\r
422 } // end func findClassTrees
\r
425 * Returns the mapping array from classnames to filenames
\r
428 * @see $classnamesToFilenames
\r
430 function getClassnamesToFilenames() {
\r
431 return $this->classnamesToFilenames;
\r
432 } // end func getClassnamesToFilenames
\r
436 * Sets the list of PHP Soucecode Files to examine.
\r
437 * @param array $phpfiles
\r
441 function setPhpSourcecodeFiles($phpfiles) {
\r
442 if (!is_array($phpfiles) || 0 == count($phpfiles))
\r
445 $this->phpfiles = $phpfiles;
\r
447 } // end func setPhpSourcecodeFiles
\r
449 } // end class PhpdocParser
\r