3 * The phpDocumentor_IntermediateParser Class
\r
5 * The Intermediary Data Parser (intermediate between Parse and Converter)
\r
7 * phpDocumentor :: automatic documentation generator
\r
9 * PHP versions 4 and 5
\r
11 * Copyright (c) 2002-2006 Gregory Beaver
\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
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
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
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
36 * @link http://www.phpdoc.org
\r
37 * @link http://pear.php.net/PhpDocumentor
\r
40 /** The phpDocumentor_IntermediateParser Class
\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
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
60 class phpDocumentor_IntermediateParser
\r
63 * @var parserDocBlock
\r
68 * type of the last parser Element handled
\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
83 * Name of the class currently being parsed.
\r
84 * It is only used (and only valid) when phpDocumentor_IntermediateParser is
\r
88 var $cur_class = '';
\r
91 * type of the current parser Element being handled
\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
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
106 var $parsePrivate = false;
\r
109 * this variable is used to prevent parsing of elements with an @ignore tag
\r
110 * @see $packageoutput
\r
111 * @see $parsePrivate
\r
113 var $private_class = false;
\r
116 * used to set the output directory
\r
117 * @see setTargetDir()
\r
122 * used to set the template base directory
\r
123 * @see setTemplateBase()
\r
128 * array of parsed package pages
\r
130 * used by {@link Convert()} to convert all package pages into output
\r
133 var $package_pages = array();
\r
136 * @var array array of all {@link parserData} containing page information
\r
138 var $pages = array();
\r
140 * Put away a page that has been @ignored or @access private if
\r
141 * !{@link $parsePrivate}
\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
151 var $privatepages = array();
\r
153 * Keeps track of packages of classes that have parent classes in another
\r
154 * package. Used in automatic linking.
\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
164 var $package_parents = array();
\r
167 * Used to determine the category for tutorials.
\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
173 var $packagecategories = array();
\r
176 * list of all packages encountered while documenting. Used in automatic
\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
183 * @var array Format: array(packagename => 1, packagename => 1,...)
\r
184 * @see Converter::getLink()
\r
186 var $all_packages = array();
\r
189 * array of packages to parser and output documentation for, if not all
\r
190 * packages should be documented
\r
193 * array(package1,package2,...)<br />
\r
194 * or false if not set
\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
206 var $packageoutput = false;
\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
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
230 * $data contains parsed structures for the current page being parsed
\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
240 * set in {@link Setup.inc.php} to the value of the quitemode commandline
\r
243 * If this option is true, informative output while parsing will not be
\r
244 * displayed (documentation is unaffected)
\r
246 * @tutorial phpDocumentor.howto.pkg#using.command-line.quiet
\r
248 var $quietMode = false;
\r
251 * set in {@link Setup.inc.php} to the value of the undocumentedElementWarnings commandline
\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
259 * @tutorial phpDocumentor.howto.pkg#using.command-line.undocumentedelements
\r
261 var $undocumentedElementWarnings = false;
\r
264 * used to keep track of inheritance at the smartest level possible for a
\r
268 var $classes = false;
\r
271 * used to keep track of all elements in a procedural page. Handles name
\r
272 * conflicts with elegance
\r
274 * @var ProceduralPages
\r
276 var $proceduralpages = false;
\r
279 * an array of template names indexed by converter name
\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
287 var $converters = false;
\r
289 * @var string Title of generated documentation, passed to Converters
\r
293 var $uses = array();
\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
303 var $ric = array();
\r
306 * Flag used to determine whether the last docblock
\r
307 * was a page-level docblock.
\r
311 var $_lastDocBlockWasPageLevel = false;
\r
314 * Flag used to determine whether the Page-level
\r
315 * DocBlock was declared in old or new style
\r
319 var $_oldPageLevel = false;
\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
326 function phpDocumentor_IntermediateParser($title='Generated Documentation')
\r
328 $this->title = $title;
\r
329 $this->data = new parserData;
\r
330 $this->classes = new Classes;
\r
331 $this->proceduralpages = new ProceduralPages;
\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
341 * @see $sourceLocation
\r
344 function _getSourceLocation($sl, $sourceloc)
\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
351 $sl = substr($sl,strpos($sl,'pear/') + 5);
\r
352 if (dirname($sl) == '.')
\r
356 return dirname($sl);
\r
359 if (strpos(str_replace($_phpDocumentor_options['Program_Root'] . PATH_DELIMITER,'',$sourceloc),PATH_DELIMITER) === false)
\r
361 return dirname(str_replace($_phpDocumentor_options['Program_Root'] . PATH_DELIMITER,'',$sourceloc));
\r
366 * Guess the package/subpackage based on subdirectory if the --pear option
\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
374 * @tutorial phpDocumentor.howto.pkg#using.command-line.pear
\r
376 function _guessPackage($path, $sourceloc)
\r
378 global $_phpDocumentor_setting;
\r
379 if ($_phpDocumentor_setting['pear'])
\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
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
393 * handles post-parsing of include/require/include_once/require_once
\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
403 function handleInclude($event,$data)
\r
405 if ($this->_lastDocBlockWasPageLevel)
\r
407 addWarning(PDERROR_DOCBLOCK_CONFLICT, $data->getName(), $data->getValue());
\r
408 if (!$this->_oldPageLevel)
\r
410 unset($this->last);
\r
413 $this->_lastDocBlockWasPageLevel = false;
\r
414 $this->data->clean = false;
\r
415 // page was @ignored
\r
416 if ($this->private_page)
\r
418 unset($this->last);
\r
421 if (empty($this->last))
\r
423 if (isset($this->db_template))
\r
425 // use the docblock template
\r
426 $this->last = phpDocumentor_clone($this->db_template);
\r
430 // we don't have a docblock, create an empty one to get rid of errors
\r
431 $this->last = new parserDocblock();
\r
434 // $this->last->setLineNumber($data->getLineNumber());
\r
435 if ($this->last->getKeyword('ignore'))
\r
437 $this->last = false;
\r
439 // addWarning(PDERROR_IGNORE_TAG_IGNORED,'include',$data->getName().'('.$data->getValue().')');
\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
449 * handles post-parsing of global variables
\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
459 function handleGlobal($event,$data)
\r
461 if ($this->_lastDocBlockWasPageLevel)
\r
463 addWarning(PDERROR_DOCBLOCK_CONFLICT, 'global variable', $data->getName());
\r
464 if (!$this->_oldPageLevel)
\r
466 unset($this->last);
\r
469 $this->_lastDocBlockWasPageLevel = false;
\r
470 $this->data->clean = false;
\r
471 if ($this->private_page)
\r
473 unset($this->last);
\r
476 if (empty($this->last))
\r
478 if (isset($this->db_template))
\r
480 // use the docblock template
\r
481 $this->last = phpDocumentor_clone($this->db_template);
\r
485 // we don't have a docblock, create an empty one to get rid of errors
\r
486 $this->last = new parserDocblock();
\r
489 // $this->last->setLineNumber($data->getLineNumber());
\r
490 if ($this->last->getKeyword('ignore'))
\r
492 addWarning(PDERROR_IGNORE_TAG_IGNORED,'global variable - just don\'t document the',$data->getName());
\r
493 $this->last = false;
\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
500 $a = $data->docblock->getKeyword('name');
\r
501 if (is_object($a)) $a = $a->value;
\r
502 $data->setName($a);
\r
504 $this->proceduralpages->addGlobal($data);
\r
505 $this->last = false;
\r
509 * handles post-parsing of Package-level documentation pages.
\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
515 function handlePackagePage($event,$data)
\r
517 $this->package_pages[$data->package] = &$data;
\r
518 $this->last = false;
\r
522 * handle post-parsing of Tutorials.
\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
530 function handleTutorial($event,$data)
\r
532 if (isset($this->packagecategories[$data->package]))
\r
534 $data->category = $this->packagecategories[$data->package];
\r
537 $data->category = $GLOBALS['phpDocumentor_DefaultCategoryName'];
\r
539 $this->tutorials[$data->package][$data->subpackage][$data->tutorial_type][$data->name] = $data;
\r
543 * handles post-parsing of class vars
\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
550 function handleVar($event,$data)
\r
552 global $_phpDocumentor_setting;
\r
553 if ($this->private_class)
\r
555 unset($this->last);
\r
558 if (empty($this->last))
\r
560 if (isset($this->db_template))
\r
562 // use the docblock template
\r
563 $this->last = phpDocumentor_clone($this->db_template);
\r
567 // we don't have a docblock, create an empty one to get rid of errors
\r
568 $this->last = new parserDocblock();
\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
575 if ($this->last->getKeyword('ignore'))
\r
577 $this->last = false;
\r
579 // addWarning(PDERROR_IGNORE_TAG_IGNORED,'var',$this->cur_class.'::'.$data->getName());
\r
581 if (!$this->last->var)
\r
583 $this->last->addVar('mixed',new parserStringWithInlineTags);
\r
586 if ($_phpDocumentor_setting['pear'])
\r
588 if (strpos($data->getName(), '_') == 1 && !$this->last->getKeyword('access'))
\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
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
602 * handles post-parsing of class constants
\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
609 function handleConst($event,$data)
\r
611 global $_phpDocumentor_setting;
\r
612 if ($this->private_class)
\r
614 unset($this->last);
\r
617 if (empty($this->last))
\r
619 if (isset($this->db_template))
\r
621 // use the docblock template
\r
622 $this->last = phpDocumentor_clone($this->db_template);
\r
626 // we don't have a docblock, create an empty one to get rid of errors
\r
627 $this->last = new parserDocblock();
\r
630 // $this->last->setLineNumber($data->getLineNumber());
\r
631 $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'const');
\r
633 if ($this->last->getKeyword('ignore'))
\r
635 $this->last = false;
\r
637 // addWarning(PDERROR_IGNORE_TAG_IGNORED,'var',$this->cur_class.'::'.$data->getName());
\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
646 * handles post-parsing of class methods
\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
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
659 function handleMethod($event,$data)
\r
661 global $_phpDocumentor_setting;
\r
662 if ($this->private_class)
\r
664 unset($this->last);
\r
668 if (empty($this->last))
\r
670 if ($this->undocumentedElementWarnings)
\r
672 addWarning(PDERROR_UNDOCUMENTED_ELEMENT,'Method',$data->getName(),'method');
\r
674 if (isset($this->db_template))
\r
676 // use the docblock template
\r
677 $this->last = phpDocumentor_clone($this->db_template);
\r
681 // we don't have a docblock, create an empty one to get rid of errors
\r
682 $this->last = new parserDocblock();
\r
685 // $this->last->setLineNumber($data->getLineNumber());
\r
686 if ($this->last->getKeyword('ignore'))
\r
688 $this->last = false;
\r
690 // addWarning(PDERROR_IGNORE_TAG_IGNORED,'method',$this->cur_class.'::'.$data->getName());
\r
692 $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'method');
\r
693 if ($data->hasSource())
\r
695 $this->last->setSource($data->getSource(), $data->getClass());
\r
697 foreach($data->listParams() as $key => $param)
\r
699 $update_params[$key] = $param;
\r
701 foreach($data->listGlobals() as $param)
\r
703 $update_globals[] = $param[1];
\r
705 foreach($data->listStatics() as $param)
\r
707 $update_statics[] = $param[0];
\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
720 if ($data->getName() == $this->cur_class) $data->setConstructor();
\r
721 if ($data->getName() == '__construct') {
\r
722 $data->setConstructor();
\r
724 if ($data->getName() == '__destruct') {
\r
725 $data->setDestructor();
\r
728 if ($_phpDocumentor_setting['pear'])
\r
730 if (strpos($data->getName(), '_') === 0 && substr($data->getName(), 1) == $data->class)
\r
732 $data->setDestructor();
\r
733 } elseif (strpos($data->getName(), '_') === 0 && !$this->last->getKeyword('access'))
\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
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
749 * handles post-parsing of functions
\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
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
763 function handleFunction($event,$data)
\r
765 if ($this->_lastDocBlockWasPageLevel)
\r
767 addWarning(PDERROR_DOCBLOCK_CONFLICT, 'function', $data->getName());
\r
768 if (!$this->_oldPageLevel)
\r
770 unset($this->last);
\r
773 $this->_lastDocBlockWasPageLevel = false;
\r
774 $this->data->clean = false;
\r
775 if ($this->private_page)
\r
777 unset($this->last);
\r
781 if (empty($this->last))
\r
783 if (isset($this->db_template))
\r
785 // use the docblock template
\r
786 $this->last = phpDocumentor_clone($this->db_template);
\r
790 // we don't have a docblock, create an empty one to get rid of errors
\r
791 $this->last = new parserDocblock();
\r
794 // $this->last->setLineNumber($data->getLineNumber());
\r
795 if ($this->last->getKeyword('ignore'))
\r
797 unset($this->last);
\r
800 $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'function');
\r
802 foreach($data->listParams() as $key => $param)
\r
804 $update_params[$key] = $param;
\r
806 foreach($data->listGlobals() as $param)
\r
808 $update_globals[] = $param[1];
\r
810 foreach($data->listStatics() as $param)
\r
812 $update_statics[] = $param[0];
\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
824 if ($data->hasSource())
\r
826 $this->last->setSource($data->getSource());
\r
828 if (count($this->last->params) == 1 && !count($data->listParams()))
\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
834 $data->setDocBlock($this->last);
\r
835 $this->proceduralpages->addFunction($data);
\r
836 $this->last = false;
\r
840 * handles post-parsing of defines
\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
849 function handleDefine($event,$data)
\r
851 if ($this->_lastDocBlockWasPageLevel)
\r
853 addWarning(PDERROR_DOCBLOCK_CONFLICT, 'define', $data->getName());
\r
854 if (!$this->_oldPageLevel)
\r
856 unset($this->last);
\r
859 $this->_lastDocBlockWasPageLevel = false;
\r
860 $this->data->clean = false;
\r
861 if ($this->private_page)
\r
863 unset($this->last);
\r
866 if (empty($this->last))
\r
868 if (isset($this->db_template))
\r
870 // use the docblock template
\r
871 $this->last = phpDocumentor_clone($this->db_template);
\r
875 // we don't have a docblock, create an empty one to get rid of errors
\r
876 $this->last = new parserDocblock();
\r
879 // $this->last->setLineNumber($data->getLineNumber());
\r
880 if ($this->last->getKeyword('ignore'))
\r
882 unset($this->last);
\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
893 * handles post-parsing of classes
\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
905 function handleClass($event,$data)
\r
907 global $_phpDocumentor_setting;
\r
908 if ($data->isInterface())
\r
910 $objectType = 'interface';
\r
914 $objectType = 'class';
\r
916 if ($this->_lastDocBlockWasPageLevel)
\r
918 if (!$this->_oldPageLevel)
\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
927 $doc->addFileSource($this->data->parent->path, $this->data->parent->source);
\r
929 $this->data->setDocBlock($doc);
\r
932 $this->last->cantSource();
\r
936 $this->_lastDocBlockWasPageLevel = false;
\r
937 $this->data->clean = false;
\r
938 if (empty($this->last))
\r
940 if ($this->undocumentedElementWarnings)
\r
942 addWarning(PDERROR_UNDOCUMENTED_ELEMENT,'Class',$data->getName(),'Class');
\r
944 if (isset($this->db_template))
\r
946 // use the docblock template
\r
947 $this->last = phpDocumentor_clone($this->db_template);
\r
951 // we don't have a docblock, create an empty one to get rid of errors
\r
952 $this->last = new parserDocblock();
\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
958 if (!$this->last->getExplicitPackage())
\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
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
969 $this->packagecategories[$this->package] = $this->category;
\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
978 $this->private_class = true;
\r
979 unset($this->last);
\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
987 $this->parsePackagePage($this->last->package, $this->data->parent->getPath());
\r
989 $this->last = false;
\r
993 * handles post-parsing of procedural pages
\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
1000 function handlePage($event,$data)
\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
1016 * handles post-parsing of DocBlocks
\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
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
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
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
1036 * If the DocBlock is page-level, it is processed with
\r
1037 * {@link _processPageLevelDocBlock}
\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
1046 function handleDocBlock($event,$data)
\r
1048 $type = 'docblock';
\r
1049 $data->postProcess();
\r
1050 // Zend desc support
\r
1051 if ($tdesc = $data->getKeyword('desc'))
\r
1053 $data->setShortDesc($tdesc);
\r
1054 unset($data->tags['desc']);
\r
1056 $this->_lastDocBlockWasPageLevel = false;
\r
1057 // 1st docblock in file, check for @package
\r
1058 if ($this->data->isClean() && !isset($this->last))
\r
1060 if ($data->getExplicitPackage())
\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
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
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
1083 $this->_processPageLevelDocBlock($this->last);
\r
1084 $this->_oldPageLevel = true;
\r
1085 $this->_lastDocBlockWasPageLevel = false;
\r
1087 $this->all_packages[$data->package] = 1;
\r
1088 $this->last = $data;
\r
1092 * Process a Page-level DocBlock
\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
1106 function _processPageLevelDocBlock($data)
\r
1108 global $_phpDocumentor_setting;
\r
1109 // can only have 1 package-level docblock, others are ignored
\r
1110 if (!$this->data->isClean())
\r
1114 $this->data->clean = false;
\r
1115 $this->data->explicitDocBlock();
\r
1116 $data->canSource();
\r
1117 if ($_phpDocumentor_setting['sourcecode'])
\r
1119 $data->addFileSource($this->data->parent->path, $this->data->parent->source);
\r
1121 if (!$data->getExplicitPackage())
\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
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
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
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
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
1150 if (is_object($access) && ($access->getString() == 'private') && (!$this->parsePrivate))
\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
1160 if ($data->getKeyword('name'))
\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
1167 $this->addPage($this->data->parent, $this->data->parent->getPath());
\r
1168 if ($this->package)
\r
1170 $this->parsePackagePage($this->package, $this->data->parent->getPath());
\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
1180 function parsePackagePage($package, $path)
\r
1182 if (!isset($this->package_pages[$package]))
\r
1184 if (file_exists(dirname($path) . SMART_PATH_DELIMITER . $package . '.html'))
\r
1186 if ($this->quietMode === false)
\r
1188 phpDocumentor_out("Reading package-level file ".$package . '.html');
\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
1195 if ($this->quietMode === false)
\r
1197 phpDocumentor_out(" -- Parsing File\n");
\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
1208 unset($pageParser);
\r
1214 * called via {@link Parser::parse()} and Parser's inherited method
\r
1215 * {@link Publisher::publishEvent()}
\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
1226 function HandleEvent($event,$data)
\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
1234 if ($data == STATE_END_CLASS)
\r
1236 } elseif ($data == PHPDOCUMENTOR_EVENT_END_PAGE)
\r
1238 if (!$this->private_page)
\r
1240 $this->all_packages[$this->package] = 1;
\r
1241 if (!$this->data->hasExplicitDocBlock())
\r
1243 $doc = $this->data->docblock;
\r
1244 if (!$this->data->docblock)
\r
1246 $doc = new parserDocBlock;
\r
1248 if ($_phpDocumentor_setting['sourcecode'])
\r
1250 $doc->canSource();
\r
1251 $doc->addFileSource($this->data->parent->path, $this->data->parent->source);
\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
1258 $this->pages[$this->data->parent->getPath()] = $this->data;
\r
1260 $this->private_page = false;
\r
1261 $this->private_class = false;
\r
1262 if (isset($this->db_template))
\r
1264 addWarning(PDERROR_DB_TEMPLATE_UNTERMINATED);
\r
1266 unset($this->db_template);
\r
1267 unset($this->last);
\r
1268 } elseif ($data == PHPDOCUMENTOR_EVENT_END_DOCBLOCK_TEMPLATE)
\r
1270 unset($this->db_template);
\r
1272 //echo $this->state_lookup[$data] . "\n";
\r
1273 //echo $data."\n";
\r
1277 if ($event == PHPDOCUMENTOR_EVENT_README_INSTALL_CHANGELOG)
\r
1279 $this->ric[$data[0]] = $data[1];
\r
1282 if ($event == PHPDOCUMENTOR_EVENT_DOCBLOCK_TEMPLATE)
\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
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
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
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
1307 if (is_object($access) && ($access->getString() == 'private') && (!$this->parsePrivate))
\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
1318 if ($this->last->getKeyword('name'))
\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
1325 $this->addPage($this->data->parent, $this->data->parent->getPath());
\r
1326 if ($this->package)
\r
1328 $this->parsePackagePage($this->package, $this->data->parent->getPath());
\r
1331 unset($this->last);
\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
1339 $data->setFile($this->data->parent->getFile());
\r
1341 $this->type = $type;
\r
1342 //echo $type . "\n";
\r
1344 if (isset($this->event_handlers[$type]))
\r
1346 $handle = $this->event_handlers[$type];
\r
1347 $this->$handle($event,$data);
\r
1354 * Replaces the {@link parserPage} represented by $this->pages[$path] with
\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
1365 * @param parserPage
\r
1366 * @param string full path to the file
\r
1368 function addPage($page, $path)
\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
1374 if (!$this->pages[$path]->docblock)
\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
1382 $this->pages[$path]->docblock->package = $page->package;
\r
1383 $this->pages[$path]->docblock->subpackage = $page->subpackage;
\r
1389 * add a new {@link parserPage} to the $pages array if none is found
\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
1398 function addPageIfNecessary($path, &$class)
\r
1400 global $_phpDocumentor_setting;
\r
1401 if (!$this->parsePrivate)
\r
1403 if (!isset($this->pages[$path]))
\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
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
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
1427 * Adds a {@link parserPage} element to the {@link parserData} element in
\r
1428 * $this->privatepages[$path]
\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
1436 function addPrivatePage($page, $path)
\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
1445 if (count($this->privatepages) == 0) {
\r
1446 $this->privatepages[$path] = new ParserData();
\r
1448 $this->privatepages[$path]->setParent($page);
\r
1449 if (isset($page->package) && $page->package != $GLOBALS['phpDocumentor_DefaultPackageName'])
\r
1451 if (!$this->privatepages[$path]->docblock)
\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
1459 $this->privatepages[$path]->docblock->package = $page->package;
\r
1460 $this->privatepages[$path]->docblock->subpackage = $page->subpackage;
\r
1466 * adds a processed descendant of {@link parserElement} to the {@link $pages}
\r
1467 * array or {@link $privatepages} array
\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
1474 function addElementToPage($element, $path)
\r
1476 if (isset($this->privatepages[$path]))
\r
1478 if (isset($this->pages[$path]))
\r
1480 if ($element->type == 'class' || $element->type == 'method'
\r
1481 || $element->type == 'var' || $element->type == 'const')
\r
1483 $this->pages[$path]->addElement($element);
\r
1485 $this->privatepages[$path]->addElement($element);
\r
1487 $this->privatepages[$path]->addElement($element);
\r
1490 if (isset($this->pages[$path]))
\r
1492 $this->pages[$path]->addElement($element);
\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
1505 function addUses($element, $path)
\r
1507 if (isset($element->type) && $element->type == 'page')
\r
1509 $element = $this->pages[$element->path];
\r
1511 if (!$this->parsePrivate && isset($element->docblock->hasaccess) && $element->docblock->hasaccess)
\r
1513 $a = $element->docblock->getKeyword('access');
\r
1514 if (is_object($a) && $a->getString() == 'private') return;
\r
1516 if (isset($this->privatepages[$path]))
\r
1518 if (isset($this->pages[$path]))
\r
1520 $uses = $element->docblock->getKeyword('uses');
\r
1523 if (!is_array($uses)) $uses = array($uses);
\r
1524 foreach($uses as $use)
\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
1535 if (isset($this->pages[$path]))
\r
1537 $uses = $element->docblock->getKeyword('uses');
\r
1540 if (!is_array($uses)) $uses = array($uses);
\r
1541 foreach($uses as $use)
\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
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
1561 function _setupUsesList(&$converter)
\r
1564 $converter->_createPkgElements($this->pages);
\r
1566 ksort($this->uses);
\r
1567 foreach($this->uses as $link => $elements)
\r
1569 foreach($elements as $element)
\r
1571 if ($element[0]->type == 'method' || $element[0]->type == 'var' ||
\r
1572 $element[0]->type == 'const')
\r
1574 $converter->class = $element[0]->getClass();
\r
1576 if ($element[0]->type == 'class')
\r
1578 $converter->class = $element[0]->getName();
\r
1580 $reallink = $converter->getLink($link,$element[0]->docblock->package);
\r
1581 if (is_object($reallink))
\r
1583 // add a used by tag to the docblock of the destination
\r
1584 switch(phpDocumentor_get_class($reallink))
\r
1587 $this->pages[$reallink->path]->docblock->addUsedBy(
\r
1588 $element[0]->getLink($converter, false, true),
\r
1591 case 'functionlink' :
\r
1592 case 'definelink' :
\r
1593 case 'globallink' :
\r
1594 if (isset($this->pages[$reallink->path]))
\r
1597 $i<count($this->pages[$reallink->path]->elements);
\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
1609 // debug('added @usedby to '.str_replace('link','',phpDocumentor_get_class($reallink)).' '.$reallink->name);
\r
1614 case 'classlink' :
\r
1615 case 'methodlink' :
\r
1617 case 'constlink' :
\r
1618 if (isset($this->pages[$reallink->path]))
\r
1620 for ($i=0;$i<count($this->pages[$reallink->path]->classelements);$i++)
\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
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
1641 * Interface to the Converter
\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
1650 function Convert($title, $converter)
\r
1652 $converter->walk($this->pages, $this->package_pages);
\r
1653 $converter->Output($title);
\r
1654 $converter->cleanup();
\r
1658 * Clean up classes
\r
1662 * @uses Classes::Inherit() passes $this
\r
1664 function fixClasses()
\r
1666 $this->classes->Inherit($this);
\r
1670 * Clean up Procedural Pages
\r
1673 * @uses ProceduralPages::setupPages() passes $this
\r
1675 function fixProcPages()
\r
1677 $this->proceduralpages->setupPages($this);
\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
1685 function addPackageParent(&$class)
\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
1695 * Add a converter name to use to the list of converters
\r
1697 * Sets up the {@link $converters} array.
\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
1708 function addConverter($output,$name,$template)
\r
1710 if ($this->templateBase) {
\r
1711 $templateBase = str_replace('\\','/', $this->templateBase) . '/Converters';
\r
1713 if ('@PEAR-DIR@' != '@'.'PEAR-DIR@') {
\r
1714 $templateBase = 'PhpDocumentor/phpDocumentor/Converters';
\r
1716 $templateBase = str_replace('\\','/',$GLOBALS['_phpDocumentor_install_dir']) . '/phpDocumentor/Converters';
\r
1719 if (strpos($name,PATH_DELIMITER))
\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
1729 include_once($filename);
\r
1732 if (!class_exists($output . $parent . 'Converter'))
\r
1734 addError(PDERROR_CONVERTER_NOT_FOUND,"parent Converter ".$output . $parent . "Converter of child Converter ".$output . str_replace(PATH_DELIMITER,'',$name) . "Converter");
\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
1742 include_once($filename);
\r
1744 if (class_exists($output . str_replace(PATH_DELIMITER,'',$name) . 'Converter'))
\r
1746 $this->converters[$output][$output . str_replace(PATH_DELIMITER,'',$name) . "Converter"][] = $template;
\r
1749 addError(PDERROR_CONVERTER_NOT_FOUND,$output . str_replace(PATH_DELIMITER,'',$name) . "Converter");
\r
1754 * does a natural case sort on two {@link parserElement} descendants
\r
1759 * @see generateElementIndex()
\r
1761 function elementCmp ($a, $b)
\r
1763 return strnatcasecmp($a->getName(), $b->getName());
\r
1767 * does a natural case sort on two class elements (either
\r
1768 * {@link parserClass, parserMethod} or {@link parserVar}
\r
1773 * @see generateElementIndex()
\r
1775 function ClasselementCmp ($a, $b)
\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
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
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
1790 return strnatcasecmp($atest,$btest);
\r
1794 * call this method once parsing has completed.
\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
1805 function Output ($title = "Generated Documentation")
\r
1807 $GLOBALS['phpDocumentor_errors']->curfile = false;
\r
1808 $this->fixClasses();
\r
1809 $this->fixProcPages();
\r
1810 // var_dump($this->uses);
\r
1812 phpDocumentor_out("\nSorting page elements...");
\r
1814 uasort($this->pages,'pagesort');
\r
1815 foreach($this->pages as $i => $page)
\r
1817 usort($this->pages[$i]->elements,array($this,'elementCmp'));
\r
1818 usort($this->pages[$i]->classelements,array($this,'ClasselementCmp'));
\r
1820 phpDocumentor_out("done\n");
\r
1822 $complicatedout = false;
\r
1823 if (is_array($this->converters))
\r
1825 if (count($this->converters) > 1)
\r
1827 $complicatedout = true;
\r
1829 phpDocumentor_out("Formatting @uses list...");
\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
1834 phpDocumentor_out("done\n\n");
\r
1836 foreach($this->converters as $converter => $blah)
\r
1838 if (is_array($blah))
\r
1840 if (count($blah) > 1)
\r
1842 $complicatedout = true;
\r
1844 foreach($blah as $converter => $templates)
\r
1846 foreach($templates as $template)
\r
1849 if ($complicatedout)
\r
1851 $extraout = SMART_PATH_DELIMITER . $converter;
\r
1853 if (count($templates) > 1)
\r
1855 $extraout .= SMART_PATH_DELIMITER . str_replace(PATH_DELIMITER, SMART_PATH_DELIMITER, substr($template,0,strlen($template) - 1));
\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
1860 $a->setTemplateBase($this->templateBase, $template);
\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
1873 addErrorDie(PDERROR_NO_CONVERTERS);
\r
1878 * Sets the output directory
\r
1880 * @param string $dir the output directory
\r
1882 function setTargetDir($dir)
\r
1884 $this->targetDir = $dir;
\r
1888 * Sets the template base directory
\r
1890 * @param string $dir the template base directory
\r
1891 * @tutorial phpDocumentor.howto.pkg#using.command-line.templatebase
\r
1893 function setTemplateBase($dir)
\r
1895 $this->templateBase = $dir;
\r
1899 * set parsing information output mode (quiet or verbose)
\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
1906 function setQuietMode($quietMode)
\r
1908 $this->quietMode = $quietMode;
\r
1912 * show warnings for undocumented elements
\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
1918 function setUndocumentedElementWarningsMode($undocumentedElementWarnings)
\r
1920 $this->undocumentedElementWarnings = $undocumentedElementWarnings;
\r
1924 * set display of elements marked with @access private
\r
1926 * If set to true, elements will be displayed
\r
1927 * @param bool $parse
\r
1929 function setParsePrivate($parse)
\r
1931 $this->parsePrivate = $parse;
\r
1935 /** @access private */
\r
1936 function pagesort($a, $b)
\r
1938 return strnatcasecmp($a->parent->file,$b->parent->file);
\r