changed git call from https to git readonly
[atutor.git] / mods / phpdoc2 / PhpDocumentor / phpDocumentor / IntermediateParser.inc
1 <?php\r
2 /**\r
3  * The phpDocumentor_IntermediateParser Class\r
4  *\r
5  * The Intermediary Data Parser (intermediate between Parse and Converter)\r
6  *\r
7  * phpDocumentor :: automatic documentation generator\r
8  * \r
9  * PHP versions 4 and 5\r
10  *\r
11  * Copyright (c) 2002-2006 Gregory Beaver\r
12  * \r
13  * LICENSE:\r
14  * \r
15  * This library is free software; you can redistribute it\r
16  * and/or modify it under the terms of the GNU Lesser General\r
17  * Public License as published by the Free Software Foundation;\r
18  * either version 2.1 of the License, or (at your option) any\r
19  * later version.\r
20  * \r
21  * This library is distributed in the hope that it will be useful,\r
22  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
23  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\r
24  * Lesser General Public License for more details.\r
25  * \r
26  * You should have received a copy of the GNU Lesser General Public\r
27  * License along with this library; if not, write to the Free Software\r
28  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\r
29  *\r
30  * @package    phpDocumentor\r
31  * @author     Gregory Beaver <cellog@php.net>\r
32  * @copyright  2002-2006 Gregory Beaver\r
33  * @license    http://www.opensource.org/licenses/lgpl-license.php LGPL\r
34  * @version    CVS: $Id: IntermediateParser.inc,v 1.21 2007/12/09 06:11:35 ashnazg Exp $\r
35  * @filesource\r
36  * @link       http://www.phpdoc.org\r
37  * @link       http://pear.php.net/PhpDocumentor\r
38  * @since      1.1\r
39  */\r
40 /** The phpDocumentor_IntermediateParser Class\r
41  *\r
42  * This class performs the work of organizing raw data from the parser in the\r
43  * format of descendants of the {@link parserElement} class.  This is also where\r
44  * processing of package pages occurs, in\r
45  * {@link phpDocumentor_IntermediateParser::handleClass()} for class-level\r
46  * packages and {@link phpDocumentor_IntermediateParser::handleDocBlock()} for\r
47  * page-level packages.\r
48  *\r
49  * Most of the work of this parser goes to matching up\r
50  * DocBlocks with the elements that they are documenting.  Since DocBlocks are\r
51  * passed before the element they document, the last DocBlock is stored in\r
52  * {@link phpDocumentor_IntermediateParser::$last} and then placed into the\r
53  * $docblock parameter of the parserElement\r
54  * descendant object.\r
55  *  @author Gregory Beaver\r
56  *  @version $Id: IntermediateParser.inc,v 1.21 2007/12/09 06:11:35 ashnazg Exp $\r
57  *  @copyright 2002 Gregory Beaver\r
58  *  @package     phpDocumentor\r
59  */\r
60 class phpDocumentor_IntermediateParser\r
61 {\r
62     /**\r
63      * @var parserDocBlock\r
64      */\r
65     var $last;\r
66     \r
67     /**\r
68      * type of the last parser Element handled\r
69      *\r
70      * This is used in handleDocBlock to determine whether a DocBlock is a\r
71      * page-level DocBlock in conjunction with the {@link parserData::$clean}\r
72      * var.  A page-level DocBlock is alwaysthe first DocBlock in a file, and\r
73      * must be followed by another DocBlock.  The first test is handled by\r
74      * parserData::$clean, which is set to false on the first encounter of an\r
75      * element, and the second test is handled by this variable, which must be\r
76      * equal to "docblock"\r
77      * @see handleDocBlock()\r
78      * @var string\r
79      */\r
80     var $lasttype = '';\r
81     \r
82     /**\r
83      * Name of the class currently being parsed.\r
84      * It is only used (and only valid) when phpDocumentor_IntermediateParser is\r
85      * parsing a class\r
86      * @var string\r
87      */\r
88     var $cur_class = '';\r
89     \r
90     /**\r
91      * type of the current parser Element being handled\r
92      * \r
93      * This is used by {@link HandleEvent()} to set the {@link $lasttype} var,\r
94      * which is used to detect page-level DocBlocks\r
95      * @var string\r
96      */\r
97     var $type = '';\r
98     \r
99     /**\r
100      * set in {@link Setup.inc.php} to the value of the parseprivate commandline\r
101      * option.  If this option is true, elements with an @access private tag\r
102      * will be parsed and displayed\r
103      * @tutorial phpDocumentor.howto.pkg#using.command-line.parseprivate\r
104      * @var boolean\r
105      */\r
106     var $parsePrivate = false;\r
107     \r
108     /**\r
109      * this variable is used to prevent parsing of elements with an @ignore tag\r
110      * @see $packageoutput\r
111      * @see $parsePrivate\r
112      */\r
113     var $private_class = false;\r
114     \r
115     /**\r
116      * used to set the output directory\r
117      * @see setTargetDir()\r
118      */\r
119     var $targetDir;\r
120     \r
121     /**\r
122      * used to set the template base directory\r
123      * @see setTemplateBase()\r
124      */\r
125     var $templateBase;\r
126     \r
127     /**\r
128      * array of parsed package pages\r
129      *\r
130      * used by {@link Convert()} to convert all package pages into output\r
131      * @var array\r
132      */\r
133     var $package_pages = array();\r
134     \r
135     /**\r
136      * @var array array of all {@link parserData} containing page information\r
137      */\r
138     var $pages = array();\r
139     /**\r
140      * Put away a page that has been @ignored or @access private if\r
141      * !{@link $parsePrivate}\r
142      *\r
143      * When a page has @access private in its DocBlock, it is placed here\r
144      * instead of in {@link $pages}, to allow for proper Class parsing.  Since\r
145      * classes and pages are parsed as if they were separate, this array allows\r
146      * public classes on private pages to retrieve information needed about the\r
147      * page that holds the class and to {@link addPageIfNecessary()} to the\r
148      * $pages array\r
149      * @var array\r
150      */\r
151     var $privatepages = array();\r
152     /**\r
153      * Keeps track of packages of classes that have parent classes in another\r
154      * package.  Used in automatic linking.\r
155      *\r
156      * This array is updated by {@link addPackageParent()}, which is called in\r
157      * {@link Classes::processChild()} to keep track of classes that descend\r
158      * from classes in different packages.  In other words, if class foo is in\r
159      * package one, and class bar is in package two, an entry\r
160      * $package_parents['two'] = 'one' will be made.\r
161      * @var array Format: packagename => parentpackagename\r
162      * @see Converter::getLink()\r
163      */\r
164     var $package_parents = array();\r
165     \r
166     /**\r
167      * Used to determine the category for tutorials.\r
168      *\r
169      * <b>WARNING:</b> If more than one category exists, the last category\r
170      * encountered will overwrite the previous and will raise a big warning\r
171      * @var array Format: packagename => categoryname\r
172      */\r
173     var $packagecategories = array();\r
174     \r
175     /**\r
176      * list of all packages encountered while documenting.  Used in automatic\r
177      * linking.\r
178      * \r
179      * Converter::getLink() first checks if an ambiguous link is found in the\r
180      * current package.  If not, it then checks in parent packages, and if still\r
181      * not found, uses this array to check in the rest of the packages before\r
182      * giving up\r
183      * @var array Format: array(packagename => 1, packagename => 1,...)\r
184      * @see Converter::getLink()\r
185      */\r
186     var $all_packages = array();\r
187     \r
188     /**\r
189      * array of packages to parser and output documentation for, if not all\r
190      * packages should be documented\r
191      *\r
192      * Format:<br />\r
193      * array(package1,package2,...)<br />\r
194      * or false if not set\r
195      *\r
196      * Use this option to limit output similar to ignoring files.  If you have\r
197      * some temporary files that you don't want to specify by name but don't\r
198      * want included in output, set a package name for all the elements in your\r
199      * project, and set packageoutput to that name.  the default package will be\r
200      * ignored.  Parsing speed does not improve.  If you want to ignore files\r
201      * for speed reasons, use the ignore command-line option\r
202      * @tutorial phpDocumentor.howto.pkg#using.command-line.packageoutput\r
203      * @see Io\r
204      * @var false|array\r
205      */\r
206     var $packageoutput = false;\r
207     \r
208     /**\r
209      * the functions which handle output from the {@link Parser}\r
210      * @see handleEvent(), handleDocBlock(), handlePage(), handleClass()\r
211      * @see handleDefine(), handleFunction(), handleMethod(), handleVar()\r
212      * @see handlePackagePage(), handleInclude(), handleTutorial()\r
213      */\r
214     var $event_handlers = array(\r
215             'docblock' => 'handleDocBlock',\r
216             'page' => 'handlePage',\r
217             'class' => 'handleClass',\r
218             'define' => 'handleDefine',\r
219             'function' => 'handleFunction',\r
220             'method' => 'handleMethod',\r
221             'var' => 'handleVar',\r
222             'const' => 'handleConst',\r
223             'packagepage' => 'handlePackagePage',\r
224             'include' => 'handleInclude',\r
225             'global' => 'handleGlobal',\r
226             'tutorial' => 'handleTutorial',\r
227             );\r
228     \r
229     /**\r
230      * $data contains parsed structures for the current page being parsed\r
231      *\r
232      * In version 1.1+, $data is only used to store the current page information.\r
233      * All handling of documented elements is handled by the\r
234      * {@link ProceduralPages} and {@link Classes} classes.\r
235      * @var parserData\r
236      */\r
237     var $data;\r
238     \r
239     /**\r
240      * set in {@link Setup.inc.php} to the value of the quitemode commandline\r
241      * option.\r
242      *\r
243      * If this option is true, informative output while parsing will not be\r
244      * displayed (documentation is unaffected)\r
245      * @var boolean\r
246      * @tutorial phpDocumentor.howto.pkg#using.command-line.quiet\r
247      */\r
248     var $quietMode = false;\r
249 \r
250     /**\r
251      * set in {@link Setup.inc.php} to the value of the undocumentedElementWarnings commandline\r
252      * option.\r
253      *\r
254      * If this option is true, warnings about certain elements (classes, methods) \r
255      * that are not documented with DocBlocks will be shown while parsing,\r
256      * and will also be displayed in the errors.html page \r
257      * (other documentation is unaffected)\r
258      * @var boolean\r
259      * @tutorial phpDocumentor.howto.pkg#using.command-line.undocumentedelements\r
260      */\r
261     var $undocumentedElementWarnings = false;\r
262         \r
263     /**\r
264      * used to keep track of inheritance at the smartest level possible for a\r
265      * dumb computer\r
266      * @var Classes\r
267      */\r
268     var $classes = false;\r
269     \r
270     /**\r
271      * used to keep track of all elements in a procedural page.  Handles name\r
272      * conflicts with elegance\r
273      * @since 1.1\r
274      * @var ProceduralPages\r
275      */\r
276     var $proceduralpages = false;\r
277     \r
278     /**\r
279      * an array of template names indexed by converter name\r
280      *\r
281      * For example, if the default HTMLframesConverter is using the DOM/l0l33t\r
282      * template, the array will be\r
283      * <code>$converters['frames'] = 'DOM/l0l33t'</code>\r
284      * @var array Format: array(Convertername1 => templatename)\r
285      * @see Converter\r
286      */\r
287     var $converters = false;\r
288     /** \r
289      * @var string Title of generated documentation, passed to Converters\r
290      */\r
291     var $title = '';\r
292     \r
293     var $uses = array();\r
294 \r
295     var $db_template;\r
296     \r
297     /**\r
298      * Stores parsed CHANGELOG/INSTALL/README files\r
299      * @var array Format: array(CHANGELOG => contents,\r
300      *                          INSTALL => contents,\r
301      *                          README => contents)\r
302      */\r
303     var $ric = array();\r
304     \r
305     /**\r
306      * Flag used to determine whether the last docblock\r
307      * was a page-level docblock.\r
308      * @var boolean\r
309      * @access private\r
310      */\r
311     var $_lastDocBlockWasPageLevel = false;\r
312     \r
313     /**\r
314      * Flag used to determine whether the Page-level\r
315      * DocBlock was declared in old or new style\r
316      * @var boolean\r
317      * @access private\r
318      */\r
319     var $_oldPageLevel = false;\r
320 \r
321     /**\r
322      * sets up basic data structures\r
323      * @param string Title of generated documentation, passed to Converters\r
324      * @see $title, $data, $classes, $proceduralpages\r
325      */\r
326     function phpDocumentor_IntermediateParser($title='Generated Documentation')\r
327     {\r
328         $this->title = $title;\r
329         $this->data = new parserData;\r
330         $this->classes = new Classes;\r
331         $this->proceduralpages = new ProceduralPages;\r
332     }\r
333     \r
334     /**\r
335      * Retrieve the relative path.  If the path contains "pear/" it will\r
336      * be used as the base, otherwise the Program_Root string will be used.\r
337      * @global array uses 'Program_Root' option to replace it with '' for\r
338      *               retrieving the source location of a file\r
339      * @param string path to file\r
340      * @return string\r
341      * @see $sourceLocation\r
342      * @access private\r
343      */\r
344     function _getSourceLocation($sl, $sourceloc)\r
345     {\r
346         global $_phpDocumentor_options;\r
347         if (empty($sl)) return false;\r
348         $sl = str_replace('\\','/',$sl);\r
349         if (strpos($sl,'pear/'))\r
350         {\r
351             $sl = substr($sl,strpos($sl,'pear/') + 5);\r
352             if (dirname($sl) == '.')\r
353             {\r
354                 return 'PEAR';\r
355             }\r
356             return dirname($sl);\r
357         } else\r
358         {\r
359             if (strpos(str_replace($_phpDocumentor_options['Program_Root'] . PATH_DELIMITER,'',$sourceloc),PATH_DELIMITER) === false)\r
360                 return '';\r
361             return dirname(str_replace($_phpDocumentor_options['Program_Root'] . PATH_DELIMITER,'',$sourceloc));\r
362         }\r
363     }\r
364     \r
365     /**\r
366      * Guess the package/subpackage based on subdirectory if the --pear option\r
367      *\r
368      * A file in pear/dir/file.php will be in package "dir."  A file in\r
369      * pear/dir/subdir/file.php will be in package "dir," subpackage "subdir."\r
370      * @param string full path of file\r
371      * @param template-ready source location Program_Root/dir/file.php\r
372      * @global array uses the 'pear' option to determine whether to guess based\r
373      *               on subdirectory\r
374      * @tutorial phpDocumentor.howto.pkg#using.command-line.pear\r
375      */\r
376     function _guessPackage($path, $sourceloc)\r
377     {\r
378         global $_phpDocumentor_setting;\r
379         if ($_phpDocumentor_setting['pear'])\r
380         {\r
381             $subpath = explode(PATH_DELIMITER, $this->_getSourceLocation($path, $sourceloc));\r
382             if (!empty($subpath[0]))\r
383             { // can only have package and subpackage in this version\r
384                 $package = $subpath[0];\r
385                 $subpackage = '';\r
386                 if (isset($subpath[1])) $subpackage = $subpath[1];\r
387                 return array($package,$subpackage);\r
388             } else return array($this->package, $this->subpackage);\r
389         } else return array($this->package, $this->subpackage);\r
390     }\r
391     \r
392     /**\r
393      * handles post-parsing of include/require/include_once/require_once\r
394      *\r
395      * This function sets {@link $data}->clean to false to tell the\r
396      * phpDocumentor_IntermediateParser that a page-level DocBlock can't be\r
397      * found after this point on this page.  It then sets the package\r
398      * to be the same as the page, and adds itself to the\r
399      * {@link ProceduralPages} class\r
400      * @param integer $event Event number from {@link Parser.inc}\r
401      * @param parserInclude $data\r
402      */\r
403     function handleInclude($event,$data)\r
404     {\r
405         if ($this->_lastDocBlockWasPageLevel)\r
406         {\r
407             addWarning(PDERROR_DOCBLOCK_CONFLICT, $data->getName(), $data->getValue());\r
408             if (!$this->_oldPageLevel)\r
409             {\r
410                 unset($this->last);\r
411             }\r
412         }\r
413         $this->_lastDocBlockWasPageLevel = false;\r
414         $this->data->clean = false;\r
415         // page was @ignored\r
416         if ($this->private_page)\r
417         {\r
418             unset($this->last);\r
419             return;\r
420         }\r
421         if (empty($this->last))\r
422         {\r
423             if (isset($this->db_template))\r
424             {\r
425                 // use the docblock template\r
426                 $this->last = phpDocumentor_clone($this->db_template);\r
427             }\r
428             else\r
429             {\r
430                 // we don't have a docblock, create an empty one to get rid of errors\r
431                 $this->last = new parserDocblock();\r
432             }\r
433         }\r
434 //        $this->last->setLineNumber($data->getLineNumber());\r
435         if ($this->last->getKeyword('ignore'))\r
436         {\r
437             $this->last = false;\r
438             return;\r
439 //            addWarning(PDERROR_IGNORE_TAG_IGNORED,'include',$data->getName().'('.$data->getValue().')');\r
440         }\r
441 \r
442         $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'include');\r
443         $data->setDocBlock($this->last);\r
444         $this->proceduralpages->addInclude($data);\r
445         $this->last = false;\r
446     }\r
447     \r
448     /**\r
449      * handles post-parsing of global variables\r
450      *\r
451      * This function sets {@link $data}->clean to false to tell the\r
452      * phpDocumentor_IntermediateParser that a page-level DocBlock can't be\r
453      * found after this point on this page.  It then sets the package\r
454      * to be the same as the page, and adds itself to the\r
455      * {@link ProceduralPages} class\r
456      * @param integer $event Event number from {@link Parser.inc}\r
457      * @param parserGlobal $data\r
458      */\r
459     function handleGlobal($event,$data)\r
460     {\r
461         if ($this->_lastDocBlockWasPageLevel)\r
462         {\r
463             addWarning(PDERROR_DOCBLOCK_CONFLICT, 'global variable', $data->getName());\r
464             if (!$this->_oldPageLevel)\r
465             {\r
466                 unset($this->last);\r
467             }\r
468         }\r
469         $this->_lastDocBlockWasPageLevel = false;\r
470         $this->data->clean = false;\r
471         if ($this->private_page)\r
472         {\r
473             unset($this->last);\r
474             return;\r
475         }\r
476         if (empty($this->last))\r
477         {\r
478             if (isset($this->db_template))\r
479             {\r
480                 // use the docblock template\r
481                 $this->last = phpDocumentor_clone($this->db_template);\r
482             }\r
483             else\r
484             {\r
485                 // we don't have a docblock, create an empty one to get rid of errors\r
486                 $this->last = new parserDocblock();\r
487             }\r
488         }\r
489 //        $this->last->setLineNumber($data->getLineNumber());\r
490         if ($this->last->getKeyword('ignore'))\r
491         {\r
492             addWarning(PDERROR_IGNORE_TAG_IGNORED,'global variable - just don\'t document the',$data->getName());\r
493             $this->last = false;\r
494             return;\r
495         }\r
496         $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'global');\r
497         $data->setDocBlock($this->last);\r
498         if ($data->docblock->getKeyword('name'))\r
499         {\r
500             $a = $data->docblock->getKeyword('name');\r
501             if (is_object($a)) $a = $a->value;\r
502             $data->setName($a);\r
503         }\r
504         $this->proceduralpages->addGlobal($data);\r
505         $this->last = false;\r
506     }\r
507     \r
508     /**\r
509      * handles post-parsing of Package-level documentation pages.\r
510      *\r
511      * sets the {@link $package_pages}[$data->package] to $data\r
512      * @param integer $event Event number from {@link Parser.inc}\r
513      * @param parserPackagePage $data\r
514      */\r
515     function handlePackagePage($event,$data)\r
516     {\r
517         $this->package_pages[$data->package] = &$data;\r
518         $this->last = false;\r
519     }\r
520     \r
521     /**\r
522      * handle post-parsing of Tutorials.\r
523      *\r
524      * This adds the parsed tutorial to the tutorial tree\r
525      * @uses $tutorials sets the value of tutorials to parameter $data\r
526      * @param integer $event Event Number\r
527      * @param parserTutorial $data\r
528      * @since 1.2\r
529      */\r
530     function handleTutorial($event,$data)\r
531     {\r
532         if (isset($this->packagecategories[$data->package]))\r
533         {\r
534             $data->category = $this->packagecategories[$data->package];\r
535         } else\r
536         {\r
537             $data->category = $GLOBALS['phpDocumentor_DefaultCategoryName'];\r
538         }\r
539         $this->tutorials[$data->package][$data->subpackage][$data->tutorial_type][$data->name] = $data;\r
540     }\r
541     \r
542     /**\r
543      * handles post-parsing of class vars\r
544      *\r
545      * This function sets up a @var tag if none is found, and aligns $data's\r
546      * $path var and packages to match the parent object\r
547      * @param integer $event Event number from {@link Parser.inc}\r
548      * @param parserVar $data\r
549      */\r
550     function handleVar($event,$data)\r
551     {\r
552         global $_phpDocumentor_setting;\r
553         if ($this->private_class)\r
554         {\r
555             unset($this->last);\r
556             return;\r
557         }\r
558         if (empty($this->last))\r
559         {\r
560             if (isset($this->db_template))\r
561             {\r
562                 // use the docblock template\r
563                 $this->last = phpDocumentor_clone($this->db_template);\r
564             } \r
565             else \r
566             {\r
567                 // we don't have a docblock, create an empty one to get rid of errors\r
568                 $this->last = new parserDocblock();\r
569             }\r
570         }\r
571 //        $this->last->setLineNumber($data->getLineNumber());\r
572         $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'var');\r
573         $this->last->updateModifiers($data->getModifiers());\r
574 \r
575         if ($this->last->getKeyword('ignore'))\r
576         {\r
577             $this->last = false;\r
578             return;\r
579 //            addWarning(PDERROR_IGNORE_TAG_IGNORED,'var',$this->cur_class.'::'.$data->getName());\r
580         }\r
581         if (!$this->last->var)\r
582         {\r
583             $this->last->addVar('mixed',new parserStringWithInlineTags);\r
584         }\r
585         \r
586         if ($_phpDocumentor_setting['pear'])\r
587         {\r
588             if (strpos($data->getName(), '_') == 1 && !$this->last->getKeyword('access'))\r
589             {\r
590                 addWarning(PDERROR_PRIVATE_ASSUMED,'class variable',$data->class.'::'.$data->getName());\r
591                 $this->last->addKeyword('access','private');\r
592                 $data->setDocBlock($this->last);\r
593             }\r
594         }\r
595         $data->setDocBlock($this->last);\r
596         $data->path = $this->data->parent->path;\r
597         $this->classes->addVar($data);\r
598         $this->last = false;\r
599     }\r
600     \r
601     /**\r
602      * handles post-parsing of class constants\r
603      *\r
604      * This function aligns $data's\r
605      * $path var and packages to match the parent object\r
606      * @param integer $event Event number from {@link Parser.inc}\r
607      * @param parserVar $data\r
608      */\r
609     function handleConst($event,$data)\r
610     {\r
611         global $_phpDocumentor_setting;\r
612         if ($this->private_class)\r
613         {\r
614             unset($this->last);\r
615             return;\r
616         }\r
617         if (empty($this->last))\r
618         {\r
619             if (isset($this->db_template))\r
620             {\r
621                 // use the docblock template\r
622                 $this->last = phpDocumentor_clone($this->db_template);\r
623             }\r
624             else\r
625             {\r
626                 // we don't have a docblock, create an empty one to get rid of errors\r
627                 $this->last = new parserDocblock();\r
628             }\r
629         }\r
630 //        $this->last->setLineNumber($data->getLineNumber());\r
631         $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'const');\r
632 \r
633         if ($this->last->getKeyword('ignore'))\r
634         {\r
635             $this->last = false;\r
636             return;\r
637 //            addWarning(PDERROR_IGNORE_TAG_IGNORED,'var',$this->cur_class.'::'.$data->getName());\r
638         }\r
639         $data->setDocBlock($this->last);\r
640         $data->path = $this->data->parent->path;\r
641         $this->classes->addConst($data);\r
642         $this->last = false;\r
643     }\r
644     \r
645     /**\r
646      * handles post-parsing of class methods\r
647      *\r
648      * This function first aligns $data's path and package to match the parent\r
649      * object, and also aligns the docblock's @param, @global, and @staticvar\r
650      * tags with the information parsed from the method source code.  It also\r
651      * checks to see if the method is a constructor and sets the $isConstructor\r
652      * flag.  If source code has been parsed by a {@}source} tag, the source is\r
653      * added to its docblock\r
654      *\r
655      * Finally, it adds the method to the {@link Classes} class.\r
656      * @param integer $event Event number from {@link Parser.inc}\r
657      * @param parserMethod $data\r
658      */\r
659     function handleMethod($event,$data)\r
660     {\r
661         global $_phpDocumentor_setting;\r
662         if ($this->private_class)\r
663         {\r
664             unset($this->last);\r
665             return;\r
666         }\r
667 \r
668         if (empty($this->last))\r
669         {\r
670                         if ($this->undocumentedElementWarnings)\r
671             {\r
672                 addWarning(PDERROR_UNDOCUMENTED_ELEMENT,'Method',$data->getName(),'method');\r
673             }           \r
674             if (isset($this->db_template))\r
675             {\r
676                 // use the docblock template\r
677                 $this->last = phpDocumentor_clone($this->db_template);\r
678             }\r
679             else\r
680             {\r
681                 // we don't have a docblock, create an empty one to get rid of errors\r
682                 $this->last = new parserDocblock();\r
683             }\r
684         }\r
685 //        $this->last->setLineNumber($data->getLineNumber());\r
686         if ($this->last->getKeyword('ignore'))\r
687         {\r
688             $this->last = false;\r
689             return;\r
690 //            addWarning(PDERROR_IGNORE_TAG_IGNORED,'method',$this->cur_class.'::'.$data->getName());\r
691         }\r
692         $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'method');\r
693         if ($data->hasSource())\r
694         {\r
695             $this->last->setSource($data->getSource(), $data->getClass());\r
696         }\r
697         foreach($data->listParams() as $key => $param)\r
698         {\r
699             $update_params[$key] = $param;\r
700         }\r
701         foreach($data->listGlobals() as $param)\r
702         {\r
703             $update_globals[] = $param[1];\r
704         }\r
705         foreach($data->listStatics() as $param)\r
706         {\r
707             $update_statics[] = $param[0];\r
708         }\r
709         if (isset($update_params))\r
710         $this->last->updateParams($update_params);\r
711         if (isset($update_globals))\r
712         $this->last->updateGlobals($update_globals);\r
713         if (isset($update_statics))\r
714         $this->last->updateStatics($update_statics);\r
715         $this->last->updateModifiers($data->getModifiers());\r
716         unset($update_params);\r
717         unset($update_globals);\r
718         unset($update_statics);\r
719 \r
720         if ($data->getName() == $this->cur_class) $data->setConstructor();\r
721         if ($data->getName() == '__construct') {\r
722             $data->setConstructor();\r
723         }\r
724         if ($data->getName() == '__destruct') {\r
725             $data->setDestructor();\r
726         }\r
727 \r
728         if ($_phpDocumentor_setting['pear'])\r
729         {\r
730             if (strpos($data->getName(), '_') === 0 && substr($data->getName(), 1) == $data->class)\r
731             { // is destructor\r
732                 $data->setDestructor();\r
733             } elseif (strpos($data->getName(), '_') === 0 && !$this->last->getKeyword('access'))\r
734             {\r
735                 if (strpos($data->getName(), '__') !== 0) {\r
736                     addWarning(PDERROR_PRIVATE_ASSUMED,'method',$data->class.'::'.$data->getName().'()');\r
737                     $this->last->addKeyword('access','private');\r
738                     $data->setDocBlock($this->last);\r
739                 }\r
740             }\r
741         }\r
742         $data->setDocBlock($this->last);\r
743         $data->path = $this->data->parent->path;\r
744         $this->classes->addMethod($data);\r
745         $this->last = false;\r
746     }\r
747 \r
748     /**\r
749      * handles post-parsing of functions\r
750      *\r
751      * This function sets {@link $data}->clean to false to tell the\r
752      * phpDocumentor_IntermediateParser that a page-level DocBlock can't be\r
753      * found after this point on this page.  It then sets the package to be the\r
754      * same as the page, aligns the docblock's @param, @global, and @staticvar\r
755      * tags with the information parsed from the function source code.\r
756      *\r
757      * If source code has been parsed by a {@}source} tag, the source is added\r
758      * to its docblock, and then the parserFunction adds itself to the\r
759      * {@link ProceduralPages} class\r
760      * @param integer $event Event number from {@link Parser.inc}\r
761      * @param parserFunction $data\r
762      */\r
763     function handleFunction($event,$data)\r
764     {\r
765         if ($this->_lastDocBlockWasPageLevel)\r
766         {\r
767             addWarning(PDERROR_DOCBLOCK_CONFLICT, 'function', $data->getName());\r
768             if (!$this->_oldPageLevel)\r
769             {\r
770                 unset($this->last);\r
771             }\r
772         }\r
773         $this->_lastDocBlockWasPageLevel = false;\r
774         $this->data->clean = false;\r
775         if ($this->private_page)\r
776         {\r
777             unset($this->last);\r
778             return;\r
779         }\r
780 \r
781         if (empty($this->last))\r
782         {\r
783             if (isset($this->db_template))\r
784             {\r
785                 // use the docblock template\r
786                 $this->last = phpDocumentor_clone($this->db_template);\r
787             }\r
788             else\r
789             {\r
790                 // we don't have a docblock, create an empty one to get rid of errors\r
791                 $this->last = new parserDocblock();\r
792             }\r
793         }\r
794 //        $this->last->setLineNumber($data->getLineNumber());\r
795         if ($this->last->getKeyword('ignore'))\r
796         {\r
797             unset($this->last);\r
798             return;\r
799         }\r
800         $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'function');\r
801 \r
802         foreach($data->listParams() as $key => $param)\r
803         {\r
804             $update_params[$key] = $param;\r
805         }\r
806         foreach($data->listGlobals() as $param)\r
807         {\r
808             $update_globals[] = $param[1];\r
809         }\r
810         foreach($data->listStatics() as $param)\r
811         {\r
812             $update_statics[] = $param[0];\r
813         }\r
814         if (isset($update_params))\r
815         $this->last->updateParams($update_params);\r
816         if (isset($update_globals))\r
817         $this->last->updateGlobals($update_globals);\r
818         if (isset($update_statics))\r
819         $this->last->updateStatics($update_statics);\r
820         unset($update_params);\r
821         unset($update_globals);\r
822         unset($update_statics);\r
823 \r
824         if ($data->hasSource())\r
825         {\r
826             $this->last->setSource($data->getSource());\r
827         }\r
828         if (count($this->last->params) == 1 && !count($data->listParams()))\r
829         {\r
830             // if the function has no parameters, and 1 @param, add it to the list as optional, default value is description from @param\r
831             $pars = $this->last->listParams();\r
832             $data->addParam($pars[0]['var'],$pars[0]['data']->getString());\r
833         }\r
834         $data->setDocBlock($this->last);\r
835         $this->proceduralpages->addFunction($data);\r
836         $this->last = false;\r
837     }\r
838     \r
839     /**\r
840      * handles post-parsing of defines\r
841      *\r
842      * This function sets {@link $data}->clean to false to tell the\r
843      * phpDocumentor_IntermediateParser that a page-level DocBlock can't be\r
844      * found after this point on this page.  It then sets the package to be the\r
845      * same as the page and adds itself to the {@link ProceduralPages} class\r
846      * @param integer $event Event number from {@link Parser.inc}\r
847      * @param parserDefine $data\r
848      */\r
849     function handleDefine($event,$data)\r
850     {\r
851         if ($this->_lastDocBlockWasPageLevel)\r
852         {\r
853             addWarning(PDERROR_DOCBLOCK_CONFLICT, 'define', $data->getName());\r
854             if (!$this->_oldPageLevel)\r
855             {\r
856                 unset($this->last);\r
857             }\r
858         }\r
859         $this->_lastDocBlockWasPageLevel = false;\r
860         $this->data->clean = false;\r
861         if ($this->private_page)\r
862         {\r
863             unset($this->last);\r
864             return;\r
865         }\r
866         if (empty($this->last))\r
867         {\r
868             if (isset($this->db_template))\r
869             {\r
870                 // use the docblock template\r
871                 $this->last = phpDocumentor_clone($this->db_template);\r
872             }\r
873             else\r
874             {\r
875                 // we don't have a docblock, create an empty one to get rid of errors\r
876                 $this->last = new parserDocblock();\r
877             }\r
878         }\r
879 //        $this->last->setLineNumber($data->getLineNumber());\r
880         if ($this->last->getKeyword('ignore'))\r
881         {\r
882             unset($this->last);\r
883             return;\r
884         }\r
885 \r
886         $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'define');\r
887         $data->setDocBlock($this->last);\r
888         $this->proceduralpages->addDefine($data);\r
889         $this->last = false;\r
890     }\r
891     \r
892     /**\r
893      * handles post-parsing of classes\r
894      *\r
895      * This function sets {@link $data}->clean to false to tell the\r
896      * phpDocumentor_IntermediateParser that a page-level DocBlock can't be\r
897      * found after this point on this page.  It sets {@link $cur_class} to its\r
898      * name, and if an @ignore tag is found in the DocBlock, it sets\r
899      * {@link $private_class} to true, to prevent post-parsing of any of the\r
900      * class's vars or methods.  Then it checks for the existence of a package\r
901      * page for the class's package\r
902      * @param integer $event Event number from {@link Parser.inc}\r
903      * @param parserClass $data\r
904      */\r
905     function handleClass($event,$data)\r
906     {\r
907         global $_phpDocumentor_setting;\r
908         if ($data->isInterface()) \r
909         {\r
910             $objectType = 'interface';\r
911         }\r
912         else\r
913         {\r
914             $objectType = 'class';\r
915         }\r
916         if ($this->_lastDocBlockWasPageLevel)\r
917         {\r
918             if (!$this->_oldPageLevel)\r
919             {\r
920                 addWarning(PDERROR_DOCBLOCK_GOES_CLASS, $data->getName());\r
921                 $doc = new parserDocBlock;\r
922                 $doc->category = $this->category;\r
923                 $doc->package = $this->package;\r
924                 $doc->subpackage = $this->subpackage;\r
925                 if ($_phpDocumentor_setting['sourcecode']) {\r
926                     $doc->canSource();\r
927                     $doc->addFileSource($this->data->parent->path, $this->data->parent->source);\r
928                 }\r
929                 $this->data->setDocBlock($doc);\r
930                 unset($doc);\r
931                 if ($this->last) {\r
932                     $this->last->cantSource();\r
933                 }\r
934             }\r
935         }\r
936         $this->_lastDocBlockWasPageLevel = false;\r
937         $this->data->clean = false;\r
938         if (empty($this->last))\r
939         {\r
940             if ($this->undocumentedElementWarnings)\r
941             {\r
942                 addWarning(PDERROR_UNDOCUMENTED_ELEMENT,'Class',$data->getName(),'Class');\r
943             }\r
944             if (isset($this->db_template))\r
945             {\r
946                 // use the docblock template\r
947                 $this->last = phpDocumentor_clone($this->db_template);\r
948             }\r
949             else\r
950             {\r
951                 // we don't have a docblock, create an empty one to get rid of errors\r
952                 $this->last = new parserDocblock();\r
953             }\r
954             list($this->last->package, $this->last->subpackage) = $this->_guessPackage($this->data->parent->path, $this->data->parent->getSourceLocation('dummy'));\r
955             addWarning(PDERROR_NO_PACKAGE_TAG,$objectType,$data->getName(),$this->last->package);\r
956         } else\r
957         {\r
958             if (!$this->last->getExplicitPackage())\r
959             {\r
960                 list($this->last->package, $this->last->subpackage) = $this->_guessPackage($this->data->parent->path, $this->data->parent->getSourceLocation('dummy'));\r
961                 addWarning(PDERROR_NO_PACKAGE_TAG,$objectType,$data->getName(),$this->last->package);\r
962             } else\r
963             {\r
964                 if (isset($this->packagecategories[$this->package])\r
965                     && $this->packagecategories[$this->package] != $this->category)\r
966                     addWarning(PDERROR_PACKAGECAT_SET,$this->package,\r
967                                 $this->packagecategories[$this->package],\r
968                                 $this->category);\r
969                 $this->packagecategories[$this->package] = $this->category;\r
970             }\r
971         }\r
972         $this->last->updateModifiers($data->getModifiers());\r
973 //        $this->last->setLineNumber($data->getLineNumber());\r
974         $data->setDocBlock($this->last);\r
975         $this->cur_class = $name = $data->getName();\r
976         if ($this->last->getKeyword('ignore'))\r
977         {\r
978             $this->private_class = true;\r
979             unset($this->last);\r
980             return;\r
981         }\r
982         $data->path = $this->data->parent->path;\r
983         $this->classes->addClass($data);\r
984         $this->private_class = false;\r
985         if ($this->last->package)\r
986         {\r
987             $this->parsePackagePage($this->last->package, $this->data->parent->getPath());\r
988         }\r
989         $this->last = false;\r
990     }\r
991     \r
992     /**\r
993      * handles post-parsing of procedural pages\r
994      *\r
995      * this event is called at the start of a new page, before the Parser knows\r
996      * whether the page will contain any procedural pages or not\r
997      * @param integer $event Event number from {@link Parser.inc}\r
998      * @param parserPage $data\r
999      */\r
1000     function handlePage($event,$data)\r
1001     {\r
1002         $type = 'page';\r
1003         $this->private_page = false;\r
1004         $this->data = new parserData;\r
1005         $data->category = $this->category = $GLOBALS['phpDocumentor_DefaultCategoryName'];\r
1006         $this->package = $GLOBALS['phpDocumentor_DefaultPackageName'];\r
1007         $this->subpackage = '';\r
1008         $this->proceduralpages->addPage($data);\r
1009         $this->data->setParent($data);\r
1010         $this->pages[$data->getPath()] = $this->data;\r
1011         $this->classes->nextFile($data->getPath());\r
1012         $this->packageoutput = $data->getPackageOutput();\r
1013     }\r
1014     \r
1015     /**\r
1016      * handles post-parsing of DocBlocks\r
1017      *\r
1018      * This function sets {@link $last} to the DocBlock represented by $data, to\r
1019      * allow the next documentable element passed to\r
1020      * phpDocumentor_IntermediateParser to link the DocBlock into its $docblock\r
1021      * property.  This function also checks for two special cases of DocBlocks:\r
1022      * <ol>\r
1023      *    <li>First DocBlock in the file contains a @package tag</li>\r
1024      *    <li>First DocBlock in the file is immediately followed by another\r
1025      *        DocBlock</li>\r
1026      * </ol>\r
1027      * In both cases, the function extracts this tag and uses it as the\r
1028      * page-level package.  If the @package tag is in the DocBlock of an\r
1029      * element (function, global variable, whatever) that isn't a page-level\r
1030      * DocBlock, a warning will be raised to notify the author that a @package\r
1031      * tag belongs in a page-level DocBlock.\r
1032      *\r
1033      * <b>New</b> in version 1.2.2, if the first DocBlock in a file contains\r
1034      * a @package tag, it is a page-level DocBlock.\r
1035      *\r
1036      * If the DocBlock is page-level, it is processed with\r
1037      * {@link _processPageLevelDocBlock}\r
1038      *\r
1039      * Finally, the function replaces the old parserPage in\r
1040      * {@link parserData::$data}->parent with the new one containing information\r
1041      * from the DocBlock by calling {@link addPage()}, and checks for\r
1042      * package-level docs.\r
1043      * @param integer $event Event number from {@link Parser.inc}\r
1044      * @param parserDocBlock $data\r
1045      */\r
1046     function handleDocBlock($event,$data)\r
1047     {\r
1048         $type = 'docblock';\r
1049         $data->postProcess();\r
1050         // Zend desc support\r
1051         if ($tdesc = $data->getKeyword('desc'))\r
1052         {\r
1053             $data->setShortDesc($tdesc);\r
1054             unset($data->tags['desc']);\r
1055         }\r
1056         $this->_lastDocBlockWasPageLevel = false;\r
1057         // 1st docblock in file, check for @package\r
1058         if ($this->data->isClean() && !isset($this->last))\r
1059         {\r
1060             if ($data->getExplicitPackage())\r
1061             {\r
1062                 // new with 1.2.2:\r
1063                 // if the first docblock in a file\r
1064                 // contains a @package tag, then it is\r
1065                 // a page-level docblock\r
1066                 $this->_processPageLevelDocBlock($data);\r
1067                 $this->_lastDocBlockWasPageLevel = true;\r
1068                 $this->all_packages[$data->package] = 1;\r
1069                 $this->last = $data;\r
1070                 return;\r
1071             }\r
1072             $doc = new parserDocBlock;\r
1073             $doc->category = $this->category;\r
1074             $doc->package = $this->package;\r
1075             $doc->subpackage = $this->subpackage;\r
1076             $this->data->setDocBlock($doc);\r
1077             $this->proceduralpages->addPagePackage($this->data->parent->getPath(),$this->package,$this->subpackage);\r
1078             unset($doc);\r
1079         }\r
1080         // 2nd docblock in a row, and it's at the top of the file, page-level docblock\r
1081         if ($this->lasttype == "docblock" && $this->data->isClean())\r
1082         {\r
1083             $this->_processPageLevelDocBlock($this->last);\r
1084             $this->_oldPageLevel = true;\r
1085             $this->_lastDocBlockWasPageLevel = false;\r
1086         }\r
1087         $this->all_packages[$data->package] = 1;\r
1088         $this->last = $data;\r
1089     }\r
1090     \r
1091     /**\r
1092      * Process a Page-level DocBlock\r
1093      *\r
1094      * First, it checks for an @ignore tag,\r
1095      * and if found, calls {@link ProceduralPages::ignorePage()}.  An @ignore\r
1096      * tag in a page-level DocBlock will ignore all functions, defines, global\r
1097      * variables, and includes.  It will not ignore classes!  The function next\r
1098      * checks for an @access private, and if --parseprivate is off, performs the\r
1099      * same actions as @ignore.\r
1100      * Next, it checks for the @name tag, which is used to rename the page.\r
1101      * This is also a PEAR compatibility issue, and may not be very useful in\r
1102      * the long run.  Documentation is best when it refers to real entities in\r
1103      * the package, and not to aliases.\r
1104      * @access private\r
1105      */\r
1106     function _processPageLevelDocBlock($data)\r
1107     {\r
1108         global $_phpDocumentor_setting;\r
1109         // can only have 1 package-level docblock, others are ignored\r
1110         if (!$this->data->isClean())\r
1111         {\r
1112             return;\r
1113         }\r
1114         $this->data->clean = false;\r
1115         $this->data->explicitDocBlock();\r
1116         $data->canSource();\r
1117         if ($_phpDocumentor_setting['sourcecode'])\r
1118         {\r
1119             $data->addFileSource($this->data->parent->path, $this->data->parent->source);\r
1120         }\r
1121         if (!$data->getExplicitPackage())\r
1122         {\r
1123             list($data->package,$data->subpackage) = $this->_guessPackage($this->data->parent->getPath(), $this->data->parent->getSourceLocation('dummy'));\r
1124             addWarning(PDERROR_NO_PACKAGE_TAG,'file',$this->data->parent->getPath(),$this->last->package);\r
1125         }\r
1126         if (isset($this->packagecategories[$this->package])\r
1127             && $this->packagecategories[$this->package] != $data->category)\r
1128             addWarning(PDERROR_PACKAGECAT_SET,$this->package,\r
1129                         $this->packagecategories[$this->package],\r
1130                         $data->category);\r
1131         $this->packagecategories[$this->package] = $data->category;\r
1132         $this->category = $this->data->parent->category = $data->category;\r
1133         $this->packagecategories[$this->package] = $this->category;\r
1134         $this->subpackage = $this->data->parent->subpackage = $data->subpackage;\r
1135         $this->data->setDocBlock($data);\r
1136         if ($data->getKeyword('ignore'))\r
1137         {\r
1138             $this->proceduralpages->ignorePage($this->data->parent);\r
1139             $this->private_page = true;\r
1140             unset($this->last);\r
1141             $this->privatepages[$this->data->parent->getPath()] = $this->data;\r
1142             unset($this->pages[$this->data->parent->getPath()]);\r
1143             return;\r
1144         }\r
1145         $this->package = $this->data->parent->package = $data->package;\r
1146         $this->subpackage = $this->data->parent->subpackage = $data->subpackage;\r
1147         $this->proceduralpages->addPagePackage($this->data->parent->getPath(),$this->package,$this->subpackage);\r
1148         if ($access = $data->getKeyword('access'))\r
1149         {\r
1150             if (is_object($access) && ($access->getString() == 'private') && (!$this->parsePrivate))\r
1151             {\r
1152                 $this->proceduralpages->ignorePage($this->data->parent);\r
1153                 $this->private_page = true;\r
1154                 unset($this->last);\r
1155                 $this->privatepages[$this->data->parent->getPath()] = $this->data;\r
1156                 unset($this->pages[$this->data->parent->getPath()]);\r
1157                 return;\r
1158             }\r
1159         }\r
1160         if ($data->getKeyword('name'))\r
1161         {\r
1162             $a = $data->getKeyword('name');\r
1163             if (is_object($a)) $a = $a->value;\r
1164             $this->data->parent->setFile($a);\r
1165             $this->proceduralpages->setName($a);\r
1166         }\r
1167         $this->addPage($this->data->parent, $this->data->parent->getPath());\r
1168         if ($this->package)\r
1169         {\r
1170             $this->parsePackagePage($this->package, $this->data->parent->getPath());\r
1171         }\r
1172     }\r
1173     \r
1174     /**\r
1175      * Backward-compatibility only, use the new tutorials for more power\r
1176      * @tutorial tutorials.pkg\r
1177      * @param string package name of package file to parse\r
1178      * @param string directory of file that contains package name\r
1179      */\r
1180     function parsePackagePage($package, $path)\r
1181     {\r
1182         if (!isset($this->package_pages[$package]))\r
1183         {\r
1184             if (file_exists(dirname($path) . SMART_PATH_DELIMITER . $package . '.html'))\r
1185             {\r
1186                 if ($this->quietMode === false)\r
1187                 {\r
1188                     phpDocumentor_out("Reading package-level file ".$package . '.html');\r
1189                           flush();\r
1190                 }\r
1191                 $fp = fopen(dirname($path) . SMART_PATH_DELIMITER . $package . '.html',"r");\r
1192                 $ret = fread($fp,filesize(dirname($path) . SMART_PATH_DELIMITER . $package . '.html'));\r
1193                 fclose($fp);\r
1194                 unset($fp);\r
1195                 if ($this->quietMode === false)\r
1196                 {\r
1197                     phpDocumentor_out(" -- Parsing File\n");\r
1198                           flush();\r
1199                 }\r
1200                 $pageParser = new ppageParser;\r
1201                 $tempp = $this->package;\r
1202                 $lp = $this->last;\r
1203                 $pageParser->subscribe('*',$this);\r
1204                 $pageParser->parse($ret,false,$package);\r
1205                 $this->package = $tempp;\r
1206                 $this->last = $lp;\r
1207                 unset($tempp);\r
1208                 unset($pageParser);\r
1209             }\r
1210         }\r
1211     }\r
1212     \r
1213     /**\r
1214      * called via {@link Parser::parse()} and Parser's inherited method\r
1215      * {@link Publisher::publishEvent()}\r
1216      *\r
1217      * $event is one of the PHPDOC constants from Parser.inc.  If it is not\r
1218      * PHPDOCUMENTOR_EVENT_NEWSTATE, then a function name is retrieved from the\r
1219      * {@link $event_handlers} array and called to handle the $data\r
1220      * @param integer $event event number from {@link Parser.inc}\r
1221      * @param mixed $data if $event is {@link PHPDOCUMENTOR_EVENT_NEWSTATE}, $data is a {@link PHP_DOC_EVENT_END_PAGE} or {@link STATE_END_CLASS},\r
1222      *                    otherwise $data is either a {@link parserDocBlock}, {@link parserPage} or descendant of {@link parserElement}\r
1223      * @global array we use 'sourcecode' to determine whether to highlight the source\r
1224      *               of the current file if it has no file-level docblock\r
1225      */\r
1226     function HandleEvent($event,$data)\r
1227     {\r
1228         global $_phpDocumentor_setting;\r
1229         global $phpDocumentor_DefaultPackageName, $phpDocumentor_DefaultCategoryName;\r
1230         if (empty($this->packagecategories))\r
1231         $this->packagecategories[$phpDocumentor_DefaultPackageName] = $phpDocumentor_DefaultCategoryName;\r
1232         if ($event == PHPDOCUMENTOR_EVENT_NEWSTATE)\r
1233         {\r
1234             if ($data == STATE_END_CLASS)\r
1235             {\r
1236             } elseif ($data == PHPDOCUMENTOR_EVENT_END_PAGE)\r
1237             {\r
1238                 if (!$this->private_page)\r
1239                 {\r
1240                     $this->all_packages[$this->package] = 1;\r
1241                     if (!$this->data->hasExplicitDocBlock())\r
1242                     {\r
1243                         $doc = $this->data->docblock;\r
1244                         if (!$this->data->docblock)\r
1245                         {\r
1246                             $doc = new parserDocBlock;\r
1247                         }\r
1248                         if ($_phpDocumentor_setting['sourcecode'])\r
1249                         {\r
1250                             $doc->canSource();\r
1251                             $doc->addFileSource($this->data->parent->path, $this->data->parent->source);\r
1252                         }\r
1253                         list($doc->package,$doc->subpackage) = $this->_guessPackage($this->data->parent->getPath(), $this->data->parent->getSourceLocation('dummy'));\r
1254                         addWarning(PDERROR_NO_PAGE_LEVELDOCBLOCK,$this->data->parent->getPath());\r
1255                         $this->data->setDocBlock($doc);\r
1256                         $this->proceduralpages->addPage($this->data->parent,$doc->package,$doc->subpackage);\r
1257                     }\r
1258                     $this->pages[$this->data->parent->getPath()] = $this->data;\r
1259                 }\r
1260                 $this->private_page = false;\r
1261                 $this->private_class = false;\r
1262                 if (isset($this->db_template))\r
1263                 {\r
1264                     addWarning(PDERROR_DB_TEMPLATE_UNTERMINATED);\r
1265                 }\r
1266                 unset($this->db_template);\r
1267                 unset($this->last);\r
1268             } elseif ($data == PHPDOCUMENTOR_EVENT_END_DOCBLOCK_TEMPLATE)\r
1269             {\r
1270                 unset($this->db_template);\r
1271             }\r
1272             //echo $this->state_lookup[$data] . "\n";\r
1273             //echo $data."\n";\r
1274         } \r
1275          else \r
1276         {\r
1277             if ($event == PHPDOCUMENTOR_EVENT_README_INSTALL_CHANGELOG)\r
1278             {\r
1279                 $this->ric[$data[0]] = $data[1];\r
1280                 return;\r
1281             }\r
1282             if ($event == PHPDOCUMENTOR_EVENT_DOCBLOCK_TEMPLATE)\r
1283             {\r
1284                 $data->postProcess();\r
1285                 $this->db_template = $data;\r
1286                 $this->_lastDocBlockWasPageLevel = false;\r
1287                 // 2nd docblock in a row, and it's at the top of the file, page-level docblock\r
1288                 if ($this->type == "docblock" && $this->data->isClean())\r
1289                 {\r
1290                     // can only have 1 package-level docblock, others are ignored\r
1291                     $this->data->clean = false;\r
1292                     if ($this->last->getKeyword('ignore'))\r
1293                     {\r
1294                         $this->proceduralpages->ignorePage($this->data->parent);\r
1295                         $this->private_page = true;\r
1296                         unset($this->last);\r
1297                         $this->privatepages[$this->data->parent->getPath()] = $this->data;\r
1298                         unset($this->pages[$this->data->parent->getPath()]);\r
1299                         return;\r
1300                     }\r
1301                     $this->data->setDocBlock($this->last);\r
1302                     $this->package = $this->data->parent->package = $this->last->package;\r
1303                     $this->subpackage = $this->data->parent->subpackage = $this->last->subpackage;\r
1304                     $this->proceduralpages->addPagePackage($this->data->parent->getPath(),$this->package,$this->subpackage);\r
1305                     if ($access = $this->last->getKeyword('access'))\r
1306                     {\r
1307                         if (is_object($access) && ($access->getString() == 'private') && (!$this->parsePrivate))\r
1308                         {\r
1309                             addWarning(PDERROR_PARSEPRIVATE, $this->data->parent->getPath());\r
1310                             $this->proceduralpages->ignorePage($this->data->parent);\r
1311                             $this->private_page = true;\r
1312                             unset($this->last);\r
1313                             $this->privatepages[$this->data->parent->getPath()] = $this->data;\r
1314                             unset($this->pages[$this->data->parent->getPath()]);\r
1315                             return;\r
1316                         }\r
1317                     }\r
1318                     if ($this->last->getKeyword('name'))\r
1319                     {\r
1320                         $a = $this->last->getKeyword('name');\r
1321                         if (is_object($a)) $a = $a->value;\r
1322                         $this->data->parent->setFile($a);\r
1323                         $this->proceduralpages->setName($a);\r
1324                     }\r
1325                     $this->addPage($this->data->parent, $this->data->parent->getPath());\r
1326                     if ($this->package)\r
1327                     {\r
1328                         $this->parsePackagePage($this->package, $this->data->parent->getPath());\r
1329                     }\r
1330                 }\r
1331                 unset($this->last);\r
1332             } else\r
1333             {\r
1334                 $this->lasttype = $this->type;\r
1335                 $type = $data->getType();\r
1336 //                fancy_debug($type,$data);\r
1337                 if (($type != 'page') && ($type != 'docblock') && ($type != 'packagepage') && ($type != 'tutorial'))\r
1338                 {\r
1339                     $data->setFile($this->data->parent->getFile());\r
1340                 }\r
1341                 $this->type = $type;\r
1342                 //echo $type . "\n";\r
1343                 \r
1344                 if (isset($this->event_handlers[$type]))\r
1345                 {\r
1346                     $handle = $this->event_handlers[$type];\r
1347                     $this->$handle($event,$data);\r
1348                 }\r
1349             } \r
1350         }\r
1351     }\r
1352     \r
1353     /**\r
1354      * Replaces the {@link parserPage} represented by $this->pages[$path] with\r
1355      * $page\r
1356      *\r
1357      * Called by {@link addPageIfNecessary(), handleDocBlock()} and\r
1358      * {@link ProceduralPages::setupPages()}, this method first checks to see if\r
1359      * the page has been added.  If not, it assumes that the page has either\r
1360      * been @ignored or set with @access private with --parseprivate off, and\r
1361      * returns {@link addPrivatePage()}.  Otherwise, it sets the pages[$path] to\r
1362      * be the parserPage $page and sets the package and subpackage to that of\r
1363      * $page\r
1364      * @see $pages\r
1365      * @param parserPage\r
1366      * @param string full path to the file\r
1367      */\r
1368     function addPage($page, $path)\r
1369     {\r
1370         if (!isset($this->pages[$path])) return $this->addPrivatePage($page, $path);\r
1371         $this->pages[$path]->setParent($page);\r
1372         if ($page->package != $GLOBALS['phpDocumentor_DefaultPackageName'])\r
1373         {\r
1374             if (!$this->pages[$path]->docblock)\r
1375             {\r
1376                 $docblock = new parserDocBlock;\r
1377                 $docblock->package = $page->package;\r
1378                 $docblock->subpackage = $page->subpackage;\r
1379                 $this->pages[$path]->docblock = $docblock;\r
1380             } else\r
1381             {\r
1382                 $this->pages[$path]->docblock->package = $page->package;\r
1383                 $this->pages[$path]->docblock->subpackage = $page->subpackage;\r
1384             }\r
1385         }\r
1386     }\r
1387     \r
1388     /**\r
1389      * add a new {@link parserPage} to the $pages array if none is found\r
1390      *\r
1391      * This method is used when a page has been @ignored or marked with @access\r
1392      * private, and a public class is in the page (a class with no @access\r
1393      * private in its DocBlock).  The method first creates a new page in the\r
1394      * {@link $pages} array and then copies path information, and calls\r
1395      * {@link addPage()} to set up packages\r
1396      * @param string full path of page\r
1397      */\r
1398     function addPageIfNecessary($path, &$class)\r
1399     {\r
1400         global $_phpDocumentor_setting;\r
1401         if (!$this->parsePrivate)\r
1402         {\r
1403             if (!isset($this->pages[$path]))\r
1404             {\r
1405                 $this->pages[$path] = new parserData;\r
1406                 $this->pages[$path]->docblock = new parserDocBlock;\r
1407                 $this->pages[$path]->docblock->package = $this->privatepages[$path]->docblock->package;\r
1408                 $this->pages[$path]->docblock->subpackage = $this->privatepages[$path]->docblock->subpackage;\r
1409                 $par = $this->privatepages[$path]->parent;\r
1410                 $this->pages[$path]->setParent($par);\r
1411                 $this->proceduralpages->addPage($par);\r
1412             }\r
1413         }\r
1414         if (!empty($_phpDocumentor_setting['packageoutput']))\r
1415             $packages = explode(',',$_phpDocumentor_setting['packageoutput']);\r
1416         if (!empty($_phpDocumentor_setting['packageoutput']) &&\r
1417             $this->pages[$path]->parent->package != $class->docblock->package &&\r
1418             !in_array($this->pages[$path]->parent->package,$packages))\r
1419         {\r
1420             $this->pages[$path]->parent->package = $class->docblock->package;\r
1421             $this->addPage($this->pages[$path]->parent, $path);\r
1422             $this->proceduralpages->addPage($this->pages[$path]->parent);\r
1423         }\r
1424     }\r
1425     \r
1426     /**\r
1427      * Adds a {@link parserPage} element to the {@link parserData} element in\r
1428      * $this->privatepages[$path]\r
1429      *\r
1430      * Performs a similar function to addPage, but adds to the\r
1431      * {@link $privatePages} array\r
1432      * @param parserPage $page\r
1433      * @param string $path full path to the page\r
1434      * @see addPage()\r
1435      */\r
1436     function addPrivatePage($page, $path)\r
1437     {\r
1438         /*\r
1439          * if privatepages is still empty,\r
1440          * we need to initialize it with an empty \r
1441          * path=>ParserData element, so that it has\r
1442          * a top-level element... otherwise the setParent() call\r
1443          * below will crap out.\r
1444          */\r
1445         if (count($this->privatepages) == 0) {\r
1446             $this->privatepages[$path] = new ParserData();\r
1447         }\r
1448         $this->privatepages[$path]->setParent($page);\r
1449         if (isset($page->package) && $page->package != $GLOBALS['phpDocumentor_DefaultPackageName'])\r
1450         {\r
1451             if (!$this->privatepages[$path]->docblock)\r
1452             {\r
1453                 $docblock = new parserDocBlock;\r
1454                 $docblock->package = $page->package;\r
1455                 $docblock->subpackage = $page->subpackage;\r
1456                 $this->privatepages[$path]->docblock = $docblock;\r
1457             } else\r
1458             {\r
1459                 $this->privatepages[$path]->docblock->package = $page->package;\r
1460                 $this->privatepages[$path]->docblock->subpackage = $page->subpackage;\r
1461             }\r
1462         }\r
1463     }\r
1464     \r
1465     /**\r
1466      * adds a processed descendant of {@link parserElement} to the {@link $pages}\r
1467      * array or {@link $privatepages} array\r
1468      *\r
1469      * This function expects the page to exist in either $pages or $privatepages.  It calls the\r
1470      * {@link parserData::addElement()} method to add $element to the page.\r
1471      * @param parserElement $element this will actually be a descendant of parserElement\r
1472      * @param string $path\r
1473      */\r
1474     function addElementToPage($element, $path)\r
1475     {\r
1476         if (isset($this->privatepages[$path]))\r
1477         {\r
1478             if (isset($this->pages[$path]))\r
1479             {\r
1480                 if ($element->type == 'class' || $element->type == 'method'\r
1481                     || $element->type == 'var' || $element->type == 'const')\r
1482                 {\r
1483                     $this->pages[$path]->addElement($element);\r
1484                 } else\r
1485                 $this->privatepages[$path]->addElement($element);\r
1486             } else\r
1487             $this->privatepages[$path]->addElement($element);\r
1488         } else\r
1489         {\r
1490             if (isset($this->pages[$path]))\r
1491             {\r
1492                 $this->pages[$path]->addElement($element);\r
1493             }\r
1494         }\r
1495     }\r
1496     \r
1497     /**\r
1498      * Add all the @uses tags from $element to the $uses array so that @usedby\r
1499      * virtual tags can be added\r
1500      * @uses parserUsesTag::getSeeElement() used to initialize {@link $uses}\r
1501      * @uses parserUsesTag::getDescription() used to initialize {@link $uses}\r
1502      * @param parserElement descendant of parserElement\r
1503      * @param string full path to the file \r
1504      */\r
1505     function addUses($element, $path)\r
1506     {\r
1507         if (isset($element->type) && $element->type == 'page')\r
1508         {\r
1509             $element = $this->pages[$element->path];\r
1510         }\r
1511         if (!$this->parsePrivate && isset($element->docblock->hasaccess) && $element->docblock->hasaccess)\r
1512         {\r
1513             $a =  $element->docblock->getKeyword('access');\r
1514             if (is_object($a) && $a->getString() == 'private') return;\r
1515         }\r
1516         if (isset($this->privatepages[$path]))\r
1517         {\r
1518             if (isset($this->pages[$path]))\r
1519             {\r
1520                 $uses = $element->docblock->getKeyword('uses');\r
1521                 if ($uses)\r
1522                 {\r
1523                     if (!is_array($uses)) $uses = array($uses);\r
1524                     foreach($uses as $use)\r
1525                     {\r
1526                         if (!is_object($use)) continue;\r
1527                         $el = $use->getSeeElement();\r
1528                         $description = $use->getDescription();\r
1529                         $this->uses[$el][] = array($element, $description);\r
1530                     }\r
1531                 }\r
1532             }\r
1533         } else\r
1534         {\r
1535             if (isset($this->pages[$path]))\r
1536             {\r
1537                 $uses = $element->docblock->getKeyword('uses');\r
1538                 if ($uses)\r
1539                 {\r
1540                     if (!is_array($uses)) $uses = array($uses);\r
1541                     foreach($uses as $use)\r
1542                     {\r
1543                         if (!is_object($use)) continue;\r
1544                         $el = $use->getSeeElement();\r
1545                         $description = $use->getDescription();\r
1546                         $this->uses[$el][] = array($element, $description);\r
1547                     }\r
1548                 }\r
1549             }\r
1550         }\r
1551     }\r
1552     \r
1553     /**\r
1554      * Add a {@link parserUsedByTag} link to every element referred to by @uses\r
1555      * @param Converter temporary converter used to retrieve abstract links\r
1556      * @uses phpDocumentor_IntermediateParser::addUses() indirectly, as\r
1557      *       addUses() sets up $uses, which is iterated over here\r
1558      * @uses $pages sets up all @usedby tags from here\r
1559      * @access private\r
1560      */\r
1561     function _setupUsesList(&$converter)\r
1562     {\r
1563         ob_start();\r
1564         $converter->_createPkgElements($this->pages);\r
1565         ob_end_clean();\r
1566         ksort($this->uses);\r
1567         foreach($this->uses as $link => $elements)\r
1568         {\r
1569             foreach($elements as $element)\r
1570             {\r
1571                 if ($element[0]->type == 'method' || $element[0]->type == 'var' ||\r
1572                     $element[0]->type == 'const')\r
1573                 {\r
1574                     $converter->class = $element[0]->getClass();\r
1575                 }\r
1576                 if ($element[0]->type == 'class')\r
1577                 {\r
1578                     $converter->class = $element[0]->getName();\r
1579                 }\r
1580                 $reallink = $converter->getLink($link,$element[0]->docblock->package);\r
1581                 if (is_object($reallink))\r
1582                 {\r
1583                     // add a used by tag to the docblock of the destination\r
1584                     switch(phpDocumentor_get_class($reallink))\r
1585                     {\r
1586                         case 'pagelink' :\r
1587                             $this->pages[$reallink->path]->docblock->addUsedBy(\r
1588                                 $element[0]->getLink($converter, false, true),\r
1589                                 $element[1]);\r
1590                         break;\r
1591                         case 'functionlink' :\r
1592                         case 'definelink' :\r
1593                         case 'globallink' :\r
1594                         if (isset($this->pages[$reallink->path]))\r
1595                         {\r
1596                             for ($i=0;\r
1597                                  $i<count($this->pages[$reallink->path]->elements);\r
1598                                  $i++) {\r
1599                                 if ($this->pages[$reallink->path]->elements[$i]->type ==\r
1600                                       str_replace('link', '',\r
1601                                       phpDocumentor_get_class($reallink)) &&\r
1602                                       $this->pages[$reallink->path]->\r
1603                                       elements[$i]->getName() \r
1604                                       == $reallink->name) {\r
1605                                     $this->pages[$reallink->path]->elements[$i]->\r
1606                                     docblock->addUsedBy(\r
1607                                         $element[0]->getLink($converter,false,true),\r
1608                                         $element[1]);\r
1609 //                                   debug('added @usedby to '.str_replace('link','',phpDocumentor_get_class($reallink)).' '.$reallink->name);\r
1610                                 }\r
1611                             }\r
1612                         }\r
1613                         break;\r
1614                         case 'classlink' :\r
1615                         case 'methodlink' :\r
1616                         case 'varlink' :\r
1617                         case 'constlink' :\r
1618                         if (isset($this->pages[$reallink->path]))\r
1619                         {\r
1620                             for ($i=0;$i<count($this->pages[$reallink->path]->classelements);$i++)\r
1621                             {\r
1622                                 if ($this->pages[$reallink->path]->classelements[$i]->type ==\r
1623                                       str_replace('link','',phpDocumentor_get_class($reallink)) &&\r
1624                                       $this->pages[$reallink->path]->classelements[$i]->getName() == $reallink->name &&\r
1625                                       (!isset($reallink->class) || \r
1626                                       $this->pages[$reallink->path]->classelements[$i]->getClass() == $reallink->class))\r
1627                                 {\r
1628                                     $this->pages[$reallink->path]->classelements[$i]->docblock->addUsedBy($element[0]->getLink($converter,false,true), $element[1]);\r
1629 //                                   debug('added @usedby to '.str_replace('link','',phpDocumentor_get_class($reallink)).' '.$reallink->name);\r
1630                                 }\r
1631                             }\r
1632                         }\r
1633                         break;\r
1634                     }\r
1635                 }\r
1636             }\r
1637         }\r
1638     }\r
1639     \r
1640     /**\r
1641      * Interface to the Converter\r
1642      *\r
1643      * This function simply passes {@link $pages} and {@link package_pages} to\r
1644      * the walk() method, and then calls the Output() method.  Note that\r
1645      * Output() is not required to do anything, and in fact doesn't in\r
1646      * HTMLframesConverter.\r
1647      * @uses Converter::walk() passes {@link $pages} and {@link $package_pages}\r
1648      * @uses Converter::Output()\r
1649      */\r
1650     function Convert($title, $converter)\r
1651     {\r
1652         $converter->walk($this->pages, $this->package_pages);\r
1653         $converter->Output($title);\r
1654         $converter->cleanup();\r
1655     }\r
1656     \r
1657     /**\r
1658      * Clean up classes\r
1659      *\r
1660      * {@source}\r
1661      * @access private\r
1662      * @uses Classes::Inherit() passes $this\r
1663      */\r
1664     function fixClasses()\r
1665     {\r
1666         $this->classes->Inherit($this);\r
1667     }\r
1668     \r
1669     /**\r
1670      * Clean up Procedural Pages\r
1671      * {@source}\r
1672      * @access private\r
1673      * @uses ProceduralPages::setupPages() passes $this\r
1674      */\r
1675     function fixProcPages()\r
1676     {\r
1677         $this->proceduralpages->setupPages($this);\r
1678     }\r
1679     \r
1680     /**\r
1681      * If the parent class of $class is in a different package, adds it to the\r
1682      * {@link $package_parents} array\r
1683      * @param parserClass &$class\r
1684      */\r
1685     function addPackageParent(&$class)\r
1686     {\r
1687         if (!is_array($class->parent)) return;\r
1688         $par = $this->classes->getClass($class->parent[1], $class->parent[0]);\r
1689         if ($class->docblock->package == $par->docblock->package) return;\r
1690         $this->package_parents[$class->docblock->package] = $par->docblock->package;\r
1691         if (!isset($this->package_parents[$par->docblock->package]) || !$this->package_parents[$par->docblock->package]) $this->package_parents[$par->docblock->package] = false;\r
1692     }\r
1693     \r
1694     /**\r
1695      * Add a converter name to use to the list of converters\r
1696      *\r
1697      * Sets up the {@link $converters} array.\r
1698      * {@internal\r
1699      * First, the Converter's file is included, and then, if successful,\r
1700      * the converter classname is tested for existance.  If all is good,\r
1701      * then the templates are added to the list of converters/templates to use}}\r
1702      * @param string $output output format (HTML, PDF, XML).  Must be all caps\r
1703      * @param string $name Converter name (frames, for example, is the name of\r
1704      *                     HTMLframesConverter)\r
1705      * @param string $template template to use, should be a relative path to the\r
1706      *                         templates dir (like DOM/default)\r
1707      */\r
1708     function addConverter($output,$name,$template)\r
1709     {\r
1710         if ($this->templateBase) {\r
1711             $templateBase = str_replace('\\','/', $this->templateBase) . '/Converters';\r
1712         } else {\r
1713             if ('@PEAR-DIR@' != '@'.'PEAR-DIR@') {\r
1714                 $templateBase = 'PhpDocumentor/phpDocumentor/Converters';\r
1715             } else {\r
1716                 $templateBase = str_replace('\\','/',$GLOBALS['_phpDocumentor_install_dir']) . '/phpDocumentor/Converters';\r
1717             }\r
1718         }\r
1719         if (strpos($name,PATH_DELIMITER))\r
1720         {\r
1721             // include the parent template\r
1722             $parent = explode(PATH_DELIMITER,$name);\r
1723             $parent = $parent[0];\r
1724             if (!class_exists($output . $parent . 'Converter')) {\r
1725                 $filename = $templateBase . '/' . $output . '/' . $parent . '/' . $output\r
1726                     . $parent . 'Converter.inc';\r
1727                 if (Io::isIncludeable($filename))\r
1728                 {\r
1729                     include_once($filename);\r
1730                 }\r
1731             }\r
1732             if (!class_exists($output . $parent . 'Converter'))\r
1733             {\r
1734                 addError(PDERROR_CONVERTER_NOT_FOUND,"parent Converter ".$output . $parent . "Converter of child Converter ".$output . str_replace(PATH_DELIMITER,'',$name) . "Converter");\r
1735             }\r
1736         }\r
1737         $filename = $templateBase .\r
1738              PATH_DELIMITER . $output . PATH_DELIMITER . $name . PATH_DELIMITER . $output .\r
1739              str_replace(PATH_DELIMITER, '', $name) . "Converter" . ".inc";\r
1740         if (Io::isIncludeable($filename))\r
1741         {\r
1742             include_once($filename);\r
1743         }\r
1744         if (class_exists($output . str_replace(PATH_DELIMITER,'',$name) . 'Converter'))\r
1745         {\r
1746             $this->converters[$output][$output . str_replace(PATH_DELIMITER,'',$name) . "Converter"][] = $template;\r
1747         } else\r
1748         {\r
1749             addError(PDERROR_CONVERTER_NOT_FOUND,$output . str_replace(PATH_DELIMITER,'',$name) . "Converter");\r
1750         }\r
1751     }\r
1752 \r
1753     /**\r
1754      * does a natural case sort on two {@link parserElement} descendants\r
1755      *\r
1756      * @param    mixed    $a\r
1757      * @param    mixed    $b\r
1758      * @return    int\r
1759      * @see        generateElementIndex()\r
1760      */\r
1761     function elementCmp ($a, $b)\r
1762     {\r
1763         return strnatcasecmp($a->getName(), $b->getName());\r
1764     }\r
1765     \r
1766     /**\r
1767      * does a natural case sort on two class elements (either\r
1768      * {@link parserClass, parserMethod} or {@link parserVar}\r
1769      *\r
1770      * @param    mixed    $a\r
1771      * @param    mixed    $b\r
1772      * @return    int\r
1773      * @see        generateElementIndex()\r
1774      */\r
1775     function ClasselementCmp ($a, $b)\r
1776     {\r
1777         if (phpDocumentor_get_class($a) == 'parserclass') $atest = $a->name; else $atest = $a->class;\r
1778         if (phpDocumentor_get_class($b) == 'parserclass') $btest = $b->name; else $btest = $b->class;\r
1779         \r
1780         if(($c = strnatcasecmp($atest, $btest)) != 0) return $c;\r
1781         if (phpDocumentor_get_class($a) != 'parserclass') $atest .= $a->name;\r
1782         if (phpDocumentor_get_class($b) != 'parserclass') $btest .= $b->name;\r
1783         if (phpDocumentor_get_class($a) == 'parsermethod' && phpDocumentor_get_class($b) == 'parsermethod')\r
1784         {\r
1785             if ($a->isConstructor) return -1;\r
1786             if ($b->isConstructor) return 1;\r
1787             if ($a->isDestructor) return -1;\r
1788             if ($b->isDestructor) return 1;\r
1789         }\r
1790         return strnatcasecmp($atest,$btest);\r
1791     }\r
1792     \r
1793     /**\r
1794      * call this method once parsing has completed.\r
1795      *\r
1796      * This method calls the private methods fixClasses and fixProcPages, both\r
1797      * of which adjust inheritance and package information based on complicated\r
1798      * post-parsing rules described in {@link ProceduralPages::setupPages()}\r
1799      * and {@link Classes::Inherit()}.  Then, it sorts elements of the $pages\r
1800      * array and calls Convert for each Converter in the $converters array\r
1801      * @see $converters\r
1802      * @see $pages\r
1803      * @see Convert()\r
1804      */\r
1805     function Output ($title = "Generated Documentation")\r
1806     {\r
1807         $GLOBALS['phpDocumentor_errors']->curfile = false;\r
1808         $this->fixClasses();\r
1809         $this->fixProcPages();\r
1810 //        var_dump($this->uses);\r
1811 //        exit;\r
1812         phpDocumentor_out("\nSorting page elements...");\r
1813         flush();\r
1814         uasort($this->pages,'pagesort');\r
1815         foreach($this->pages as $i => $page)\r
1816         {\r
1817             usort($this->pages[$i]->elements,array($this,'elementCmp'));\r
1818             usort($this->pages[$i]->classelements,array($this,'ClasselementCmp'));\r
1819         }\r
1820         phpDocumentor_out("done\n");\r
1821         flush();\r
1822         $complicatedout = false;\r
1823         if (is_array($this->converters))\r
1824         {\r
1825             if (count($this->converters) > 1)\r
1826             {\r
1827                 $complicatedout = true;\r
1828             }\r
1829             phpDocumentor_out("Formatting @uses list...");\r
1830             flush();\r
1831             $a = new __dummyConverter($this->all_packages, $this->package_parents, $this->classes, $this->proceduralpages, $this->packageoutput, $this->parsePrivate, $this->quietMode, $this->targetDir , '', $this->title);\r
1832             $this->_setupUsesList($a);\r
1833             unset($a);\r
1834             phpDocumentor_out("done\n\n");\r
1835             flush();\r
1836             foreach($this->converters as $converter => $blah)\r
1837             {\r
1838                 if (is_array($blah))\r
1839                 {\r
1840                     if (count($blah) > 1)\r
1841                     {\r
1842                         $complicatedout = true;\r
1843                     }\r
1844                     foreach($blah as $converter => $templates)\r
1845                     {\r
1846                         foreach($templates as $template)\r
1847                         {\r
1848                             $extraout = '';\r
1849                             if ($complicatedout)\r
1850                             {\r
1851                                 $extraout = SMART_PATH_DELIMITER . $converter;\r
1852                             }\r
1853                             if (count($templates) > 1)\r
1854                             {\r
1855                                 $extraout .= SMART_PATH_DELIMITER . str_replace(PATH_DELIMITER, SMART_PATH_DELIMITER, substr($template,0,strlen($template) - 1));\r
1856                             }\r
1857                             $a = new $converter($this->all_packages, $this->package_parents, $this->classes, $this->proceduralpages, $this->packageoutput, $this->parsePrivate, $this->quietMode, $this->targetDir . $extraout, $template, $this->title);\r
1858                             if (isset($this->templateBase))\r
1859                             {\r
1860                                 $a->setTemplateBase($this->templateBase, $template);\r
1861                             }\r
1862                             $a->ric = $this->ric;\r
1863                             $a->packagecategories = $this->packagecategories;\r
1864                             if (isset($this->tutorials)) $a->setTutorials($this->tutorials);\r
1865                             $this->Convert($title, $a);\r
1866                             unset($a);\r
1867                         }\r
1868                     }\r
1869                 }\r
1870             }\r
1871         } else\r
1872         {\r
1873             addErrorDie(PDERROR_NO_CONVERTERS);\r
1874         }\r
1875     }\r
1876 \r
1877     /**\r
1878      * Sets the output directory\r
1879      *\r
1880      * @param string $dir the output directory\r
1881      */\r
1882     function setTargetDir($dir)\r
1883     {\r
1884         $this->targetDir = $dir;\r
1885     }\r
1886 \r
1887     /**\r
1888      * Sets the template base directory\r
1889      *\r
1890      * @param string $dir the template base directory\r
1891      * @tutorial phpDocumentor.howto.pkg#using.command-line.templatebase\r
1892      */\r
1893     function setTemplateBase($dir)\r
1894     {\r
1895         $this->templateBase = $dir;\r
1896     }\r
1897 \r
1898     /**\r
1899      * set parsing information output mode (quiet or verbose)\r
1900      *\r
1901      * If set to false, no parsing information (parsing /php/file/thisfile.php,\r
1902      * Converting etc.) will be displayed.\r
1903      * Useful for cron jobs\r
1904      * @param    bool $quietMode\r
1905      */\r
1906     function setQuietMode($quietMode)\r
1907     {\r
1908         $this->quietMode = $quietMode;\r
1909     }\r
1910 \r
1911     /**\r
1912      * show warnings for undocumented elements\r
1913      *\r
1914      * If set to false, no warnings will be shown for undocumented elements.\r
1915      * Useful for identifying classes and methods that haven't yet been documented.\r
1916      * @param    bool $undocumentedElementWarnings\r
1917      */\r
1918     function setUndocumentedElementWarningsMode($undocumentedElementWarnings)\r
1919     {\r
1920         $this->undocumentedElementWarnings = $undocumentedElementWarnings;\r
1921     }\r
1922     \r
1923     /**\r
1924      * set display of elements marked with @access private\r
1925      *\r
1926      * If set to true, elements will be displayed\r
1927      * @param    bool $parse\r
1928      */\r
1929     function setParsePrivate($parse)\r
1930     {\r
1931         $this->parsePrivate = $parse;\r
1932     }\r
1933 }\r
1934 \r
1935 /** @access private */\r
1936 function pagesort($a, $b)\r
1937 {\r
1938     return strnatcasecmp($a->parent->file,$b->parent->file);\r
1939 }\r
1940 ?>\r