diff options
Diffstat (limited to 'buildscripts/PhpDocumentor/phpDocumentor/IntermediateParser.inc')
-rwxr-xr-x | buildscripts/PhpDocumentor/phpDocumentor/IntermediateParser.inc | 1940 |
1 files changed, 1940 insertions, 0 deletions
diff --git a/buildscripts/PhpDocumentor/phpDocumentor/IntermediateParser.inc b/buildscripts/PhpDocumentor/phpDocumentor/IntermediateParser.inc new file mode 100755 index 00000000..98b07159 --- /dev/null +++ b/buildscripts/PhpDocumentor/phpDocumentor/IntermediateParser.inc @@ -0,0 +1,1940 @@ +<?php +/** + * The phpDocumentor_IntermediateParser Class + * + * The Intermediary Data Parser (intermediate between Parse and Converter) + * + * phpDocumentor :: automatic documentation generator + * + * PHP versions 4 and 5 + * + * Copyright (c) 2002-2006 Gregory Beaver + * + * LICENSE: + * + * This library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General + * Public License as published by the Free Software Foundation; + * either version 2.1 of the License, or (at your option) any + * later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * @package phpDocumentor + * @author Gregory Beaver <cellog@php.net> + * @copyright 2002-2006 Gregory Beaver + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @version CVS: $Id: IntermediateParser.inc 247821 2007-12-09 06:11:35Z ashnazg $ + * @filesource + * @link http://www.phpdoc.org + * @link http://pear.php.net/PhpDocumentor + * @since 1.1 + */ +/** The phpDocumentor_IntermediateParser Class + * + * This class performs the work of organizing raw data from the parser in the + * format of descendants of the {@link parserElement} class. This is also where + * processing of package pages occurs, in + * {@link phpDocumentor_IntermediateParser::handleClass()} for class-level + * packages and {@link phpDocumentor_IntermediateParser::handleDocBlock()} for + * page-level packages. + * + * Most of the work of this parser goes to matching up + * DocBlocks with the elements that they are documenting. Since DocBlocks are + * passed before the element they document, the last DocBlock is stored in + * {@link phpDocumentor_IntermediateParser::$last} and then placed into the + * $docblock parameter of the parserElement + * descendant object. + * @author Gregory Beaver + * @version $Id: IntermediateParser.inc 247821 2007-12-09 06:11:35Z ashnazg $ + * @copyright 2002 Gregory Beaver + * @package phpDocumentor + */ +class phpDocumentor_IntermediateParser +{ + /** + * @var parserDocBlock + */ + var $last; + + /** + * type of the last parser Element handled + * + * This is used in handleDocBlock to determine whether a DocBlock is a + * page-level DocBlock in conjunction with the {@link parserData::$clean} + * var. A page-level DocBlock is alwaysthe first DocBlock in a file, and + * must be followed by another DocBlock. The first test is handled by + * parserData::$clean, which is set to false on the first encounter of an + * element, and the second test is handled by this variable, which must be + * equal to "docblock" + * @see handleDocBlock() + * @var string + */ + var $lasttype = ''; + + /** + * Name of the class currently being parsed. + * It is only used (and only valid) when phpDocumentor_IntermediateParser is + * parsing a class + * @var string + */ + var $cur_class = ''; + + /** + * type of the current parser Element being handled + * + * This is used by {@link HandleEvent()} to set the {@link $lasttype} var, + * which is used to detect page-level DocBlocks + * @var string + */ + var $type = ''; + + /** + * set in {@link Setup.inc.php} to the value of the parseprivate commandline + * option. If this option is true, elements with an @access private tag + * will be parsed and displayed + * @tutorial phpDocumentor.howto.pkg#using.command-line.parseprivate + * @var boolean + */ + var $parsePrivate = false; + + /** + * this variable is used to prevent parsing of elements with an @ignore tag + * @see $packageoutput + * @see $parsePrivate + */ + var $private_class = false; + + /** + * used to set the output directory + * @see setTargetDir() + */ + var $targetDir; + + /** + * used to set the template base directory + * @see setTemplateBase() + */ + var $templateBase; + + /** + * array of parsed package pages + * + * used by {@link Convert()} to convert all package pages into output + * @var array + */ + var $package_pages = array(); + + /** + * @var array array of all {@link parserData} containing page information + */ + var $pages = array(); + /** + * Put away a page that has been @ignored or @access private if + * !{@link $parsePrivate} + * + * When a page has @access private in its DocBlock, it is placed here + * instead of in {@link $pages}, to allow for proper Class parsing. Since + * classes and pages are parsed as if they were separate, this array allows + * public classes on private pages to retrieve information needed about the + * page that holds the class and to {@link addPageIfNecessary()} to the + * $pages array + * @var array + */ + var $privatepages = array(); + /** + * Keeps track of packages of classes that have parent classes in another + * package. Used in automatic linking. + * + * This array is updated by {@link addPackageParent()}, which is called in + * {@link Classes::processChild()} to keep track of classes that descend + * from classes in different packages. In other words, if class foo is in + * package one, and class bar is in package two, an entry + * $package_parents['two'] = 'one' will be made. + * @var array Format: packagename => parentpackagename + * @see Converter::getLink() + */ + var $package_parents = array(); + + /** + * Used to determine the category for tutorials. + * + * <b>WARNING:</b> If more than one category exists, the last category + * encountered will overwrite the previous and will raise a big warning + * @var array Format: packagename => categoryname + */ + var $packagecategories = array(); + + /** + * list of all packages encountered while documenting. Used in automatic + * linking. + * + * Converter::getLink() first checks if an ambiguous link is found in the + * current package. If not, it then checks in parent packages, and if still + * not found, uses this array to check in the rest of the packages before + * giving up + * @var array Format: array(packagename => 1, packagename => 1,...) + * @see Converter::getLink() + */ + var $all_packages = array(); + + /** + * array of packages to parser and output documentation for, if not all + * packages should be documented + * + * Format:<br /> + * array(package1,package2,...)<br /> + * or false if not set + * + * Use this option to limit output similar to ignoring files. If you have + * some temporary files that you don't want to specify by name but don't + * want included in output, set a package name for all the elements in your + * project, and set packageoutput to that name. the default package will be + * ignored. Parsing speed does not improve. If you want to ignore files + * for speed reasons, use the ignore command-line option + * @tutorial phpDocumentor.howto.pkg#using.command-line.packageoutput + * @see Io + * @var false|array + */ + var $packageoutput = false; + + /** + * the functions which handle output from the {@link Parser} + * @see handleEvent(), handleDocBlock(), handlePage(), handleClass() + * @see handleDefine(), handleFunction(), handleMethod(), handleVar() + * @see handlePackagePage(), handleInclude(), handleTutorial() + */ + var $event_handlers = array( + 'docblock' => 'handleDocBlock', + 'page' => 'handlePage', + 'class' => 'handleClass', + 'define' => 'handleDefine', + 'function' => 'handleFunction', + 'method' => 'handleMethod', + 'var' => 'handleVar', + 'const' => 'handleConst', + 'packagepage' => 'handlePackagePage', + 'include' => 'handleInclude', + 'global' => 'handleGlobal', + 'tutorial' => 'handleTutorial', + ); + + /** + * $data contains parsed structures for the current page being parsed + * + * In version 1.1+, $data is only used to store the current page information. + * All handling of documented elements is handled by the + * {@link ProceduralPages} and {@link Classes} classes. + * @var parserData + */ + var $data; + + /** + * set in {@link Setup.inc.php} to the value of the quitemode commandline + * option. + * + * If this option is true, informative output while parsing will not be + * displayed (documentation is unaffected) + * @var boolean + * @tutorial phpDocumentor.howto.pkg#using.command-line.quiet + */ + var $quietMode = false; + + /** + * set in {@link Setup.inc.php} to the value of the undocumentedElementWarnings commandline + * option. + * + * If this option is true, warnings about certain elements (classes, methods) + * that are not documented with DocBlocks will be shown while parsing, + * and will also be displayed in the errors.html page + * (other documentation is unaffected) + * @var boolean + * @tutorial phpDocumentor.howto.pkg#using.command-line.undocumentedelements + */ + var $undocumentedElementWarnings = false; + + /** + * used to keep track of inheritance at the smartest level possible for a + * dumb computer + * @var Classes + */ + var $classes = false; + + /** + * used to keep track of all elements in a procedural page. Handles name + * conflicts with elegance + * @since 1.1 + * @var ProceduralPages + */ + var $proceduralpages = false; + + /** + * an array of template names indexed by converter name + * + * For example, if the default HTMLframesConverter is using the DOM/l0l33t + * template, the array will be + * <code>$converters['frames'] = 'DOM/l0l33t'</code> + * @var array Format: array(Convertername1 => templatename) + * @see Converter + */ + var $converters = false; + /** + * @var string Title of generated documentation, passed to Converters + */ + var $title = ''; + + var $uses = array(); + + var $db_template; + + /** + * Stores parsed CHANGELOG/INSTALL/README files + * @var array Format: array(CHANGELOG => contents, + * INSTALL => contents, + * README => contents) + */ + var $ric = array(); + + /** + * Flag used to determine whether the last docblock + * was a page-level docblock. + * @var boolean + * @access private + */ + var $_lastDocBlockWasPageLevel = false; + + /** + * Flag used to determine whether the Page-level + * DocBlock was declared in old or new style + * @var boolean + * @access private + */ + var $_oldPageLevel = false; + + /** + * sets up basic data structures + * @param string Title of generated documentation, passed to Converters + * @see $title, $data, $classes, $proceduralpages + */ + function phpDocumentor_IntermediateParser($title='Generated Documentation') + { + $this->title = $title; + $this->data = new parserData; + $this->classes = new Classes; + $this->proceduralpages = new ProceduralPages; + } + + /** + * Retrieve the relative path. If the path contains "pear/" it will + * be used as the base, otherwise the Program_Root string will be used. + * @global array uses 'Program_Root' option to replace it with '' for + * retrieving the source location of a file + * @param string path to file + * @return string + * @see $sourceLocation + * @access private + */ + function _getSourceLocation($sl, $sourceloc) + { + global $_phpDocumentor_options; + if (empty($sl)) return false; + $sl = str_replace('\\','/',$sl); + if (strpos($sl,'pear/')) + { + $sl = substr($sl,strpos($sl,'pear/') + 5); + if (dirname($sl) == '.') + { + return 'PEAR'; + } + return dirname($sl); + } else + { + if (strpos(str_replace($_phpDocumentor_options['Program_Root'] . PATH_DELIMITER,'',$sourceloc),PATH_DELIMITER) === false) + return ''; + return dirname(str_replace($_phpDocumentor_options['Program_Root'] . PATH_DELIMITER,'',$sourceloc)); + } + } + + /** + * Guess the package/subpackage based on subdirectory if the --pear option + * + * A file in pear/dir/file.php will be in package "dir." A file in + * pear/dir/subdir/file.php will be in package "dir," subpackage "subdir." + * @param string full path of file + * @param template-ready source location Program_Root/dir/file.php + * @global array uses the 'pear' option to determine whether to guess based + * on subdirectory + * @tutorial phpDocumentor.howto.pkg#using.command-line.pear + */ + function _guessPackage($path, $sourceloc) + { + global $_phpDocumentor_setting; + if ($_phpDocumentor_setting['pear']) + { + $subpath = explode(PATH_DELIMITER, $this->_getSourceLocation($path, $sourceloc)); + if (!empty($subpath[0])) + { // can only have package and subpackage in this version + $package = $subpath[0]; + $subpackage = ''; + if (isset($subpath[1])) $subpackage = $subpath[1]; + return array($package,$subpackage); + } else return array($this->package, $this->subpackage); + } else return array($this->package, $this->subpackage); + } + + /** + * handles post-parsing of include/require/include_once/require_once + * + * This function sets {@link $data}->clean to false to tell the + * phpDocumentor_IntermediateParser that a page-level DocBlock can't be + * found after this point on this page. It then sets the package + * to be the same as the page, and adds itself to the + * {@link ProceduralPages} class + * @param integer $event Event number from {@link Parser.inc} + * @param parserInclude $data + */ + function handleInclude($event,$data) + { + if ($this->_lastDocBlockWasPageLevel) + { + addWarning(PDERROR_DOCBLOCK_CONFLICT, $data->getName(), $data->getValue()); + if (!$this->_oldPageLevel) + { + unset($this->last); + } + } + $this->_lastDocBlockWasPageLevel = false; + $this->data->clean = false; + // page was @ignored + if ($this->private_page) + { + unset($this->last); + return; + } + if (empty($this->last)) + { + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + } +// $this->last->setLineNumber($data->getLineNumber()); + if ($this->last->getKeyword('ignore')) + { + $this->last = false; + return; +// addWarning(PDERROR_IGNORE_TAG_IGNORED,'include',$data->getName().'('.$data->getValue().')'); + } + + $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'include'); + $data->setDocBlock($this->last); + $this->proceduralpages->addInclude($data); + $this->last = false; + } + + /** + * handles post-parsing of global variables + * + * This function sets {@link $data}->clean to false to tell the + * phpDocumentor_IntermediateParser that a page-level DocBlock can't be + * found after this point on this page. It then sets the package + * to be the same as the page, and adds itself to the + * {@link ProceduralPages} class + * @param integer $event Event number from {@link Parser.inc} + * @param parserGlobal $data + */ + function handleGlobal($event,$data) + { + if ($this->_lastDocBlockWasPageLevel) + { + addWarning(PDERROR_DOCBLOCK_CONFLICT, 'global variable', $data->getName()); + if (!$this->_oldPageLevel) + { + unset($this->last); + } + } + $this->_lastDocBlockWasPageLevel = false; + $this->data->clean = false; + if ($this->private_page) + { + unset($this->last); + return; + } + if (empty($this->last)) + { + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + } +// $this->last->setLineNumber($data->getLineNumber()); + if ($this->last->getKeyword('ignore')) + { + addWarning(PDERROR_IGNORE_TAG_IGNORED,'global variable - just don\'t document the',$data->getName()); + $this->last = false; + return; + } + $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'global'); + $data->setDocBlock($this->last); + if ($data->docblock->getKeyword('name')) + { + $a = $data->docblock->getKeyword('name'); + if (is_object($a)) $a = $a->value; + $data->setName($a); + } + $this->proceduralpages->addGlobal($data); + $this->last = false; + } + + /** + * handles post-parsing of Package-level documentation pages. + * + * sets the {@link $package_pages}[$data->package] to $data + * @param integer $event Event number from {@link Parser.inc} + * @param parserPackagePage $data + */ + function handlePackagePage($event,$data) + { + $this->package_pages[$data->package] = &$data; + $this->last = false; + } + + /** + * handle post-parsing of Tutorials. + * + * This adds the parsed tutorial to the tutorial tree + * @uses $tutorials sets the value of tutorials to parameter $data + * @param integer $event Event Number + * @param parserTutorial $data + * @since 1.2 + */ + function handleTutorial($event,$data) + { + if (isset($this->packagecategories[$data->package])) + { + $data->category = $this->packagecategories[$data->package]; + } else + { + $data->category = $GLOBALS['phpDocumentor_DefaultCategoryName']; + } + $this->tutorials[$data->package][$data->subpackage][$data->tutorial_type][$data->name] = $data; + } + + /** + * handles post-parsing of class vars + * + * This function sets up a @var tag if none is found, and aligns $data's + * $path var and packages to match the parent object + * @param integer $event Event number from {@link Parser.inc} + * @param parserVar $data + */ + function handleVar($event,$data) + { + global $_phpDocumentor_setting; + if ($this->private_class) + { + unset($this->last); + return; + } + if (empty($this->last)) + { + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + } +// $this->last->setLineNumber($data->getLineNumber()); + $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'var'); + $this->last->updateModifiers($data->getModifiers()); + + if ($this->last->getKeyword('ignore')) + { + $this->last = false; + return; +// addWarning(PDERROR_IGNORE_TAG_IGNORED,'var',$this->cur_class.'::'.$data->getName()); + } + if (!$this->last->var) + { + $this->last->addVar('mixed',new parserStringWithInlineTags); + } + + if ($_phpDocumentor_setting['pear']) + { + if (strpos($data->getName(), '_') == 1 && !$this->last->getKeyword('access')) + { + addWarning(PDERROR_PRIVATE_ASSUMED,'class variable',$data->class.'::'.$data->getName()); + $this->last->addKeyword('access','private'); + $data->setDocBlock($this->last); + } + } + $data->setDocBlock($this->last); + $data->path = $this->data->parent->path; + $this->classes->addVar($data); + $this->last = false; + } + + /** + * handles post-parsing of class constants + * + * This function aligns $data's + * $path var and packages to match the parent object + * @param integer $event Event number from {@link Parser.inc} + * @param parserVar $data + */ + function handleConst($event,$data) + { + global $_phpDocumentor_setting; + if ($this->private_class) + { + unset($this->last); + return; + } + if (empty($this->last)) + { + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + } +// $this->last->setLineNumber($data->getLineNumber()); + $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'const'); + + if ($this->last->getKeyword('ignore')) + { + $this->last = false; + return; +// addWarning(PDERROR_IGNORE_TAG_IGNORED,'var',$this->cur_class.'::'.$data->getName()); + } + $data->setDocBlock($this->last); + $data->path = $this->data->parent->path; + $this->classes->addConst($data); + $this->last = false; + } + + /** + * handles post-parsing of class methods + * + * This function first aligns $data's path and package to match the parent + * object, and also aligns the docblock's @param, @global, and @staticvar + * tags with the information parsed from the method source code. It also + * checks to see if the method is a constructor and sets the $isConstructor + * flag. If source code has been parsed by a {@}source} tag, the source is + * added to its docblock + * + * Finally, it adds the method to the {@link Classes} class. + * @param integer $event Event number from {@link Parser.inc} + * @param parserMethod $data + */ + function handleMethod($event,$data) + { + global $_phpDocumentor_setting; + if ($this->private_class) + { + unset($this->last); + return; + } + + if (empty($this->last)) + { + if ($this->undocumentedElementWarnings) + { + addWarning(PDERROR_UNDOCUMENTED_ELEMENT,'Method',$data->getName(),'method'); + } + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + } +// $this->last->setLineNumber($data->getLineNumber()); + if ($this->last->getKeyword('ignore')) + { + $this->last = false; + return; +// addWarning(PDERROR_IGNORE_TAG_IGNORED,'method',$this->cur_class.'::'.$data->getName()); + } + $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'method'); + if ($data->hasSource()) + { + $this->last->setSource($data->getSource(), $data->getClass()); + } + foreach($data->listParams() as $key => $param) + { + $update_params[$key] = $param; + } + foreach($data->listGlobals() as $param) + { + $update_globals[] = $param[1]; + } + foreach($data->listStatics() as $param) + { + $update_statics[] = $param[0]; + } + if (isset($update_params)) + $this->last->updateParams($update_params); + if (isset($update_globals)) + $this->last->updateGlobals($update_globals); + if (isset($update_statics)) + $this->last->updateStatics($update_statics); + $this->last->updateModifiers($data->getModifiers()); + unset($update_params); + unset($update_globals); + unset($update_statics); + + if ($data->getName() == $this->cur_class) $data->setConstructor(); + if ($data->getName() == '__construct') { + $data->setConstructor(); + } + if ($data->getName() == '__destruct') { + $data->setDestructor(); + } + + if ($_phpDocumentor_setting['pear']) + { + if (strpos($data->getName(), '_') === 0 && substr($data->getName(), 1) == $data->class) + { // is destructor + $data->setDestructor(); + } elseif (strpos($data->getName(), '_') === 0 && !$this->last->getKeyword('access')) + { + if (strpos($data->getName(), '__') !== 0) { + addWarning(PDERROR_PRIVATE_ASSUMED,'method',$data->class.'::'.$data->getName().'()'); + $this->last->addKeyword('access','private'); + $data->setDocBlock($this->last); + } + } + } + $data->setDocBlock($this->last); + $data->path = $this->data->parent->path; + $this->classes->addMethod($data); + $this->last = false; + } + + /** + * handles post-parsing of functions + * + * This function sets {@link $data}->clean to false to tell the + * phpDocumentor_IntermediateParser that a page-level DocBlock can't be + * found after this point on this page. It then sets the package to be the + * same as the page, aligns the docblock's @param, @global, and @staticvar + * tags with the information parsed from the function source code. + * + * If source code has been parsed by a {@}source} tag, the source is added + * to its docblock, and then the parserFunction adds itself to the + * {@link ProceduralPages} class + * @param integer $event Event number from {@link Parser.inc} + * @param parserFunction $data + */ + function handleFunction($event,$data) + { + if ($this->_lastDocBlockWasPageLevel) + { + addWarning(PDERROR_DOCBLOCK_CONFLICT, 'function', $data->getName()); + if (!$this->_oldPageLevel) + { + unset($this->last); + } + } + $this->_lastDocBlockWasPageLevel = false; + $this->data->clean = false; + if ($this->private_page) + { + unset($this->last); + return; + } + + if (empty($this->last)) + { + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + } +// $this->last->setLineNumber($data->getLineNumber()); + if ($this->last->getKeyword('ignore')) + { + unset($this->last); + return; + } + $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'function'); + + foreach($data->listParams() as $key => $param) + { + $update_params[$key] = $param; + } + foreach($data->listGlobals() as $param) + { + $update_globals[] = $param[1]; + } + foreach($data->listStatics() as $param) + { + $update_statics[] = $param[0]; + } + if (isset($update_params)) + $this->last->updateParams($update_params); + if (isset($update_globals)) + $this->last->updateGlobals($update_globals); + if (isset($update_statics)) + $this->last->updateStatics($update_statics); + unset($update_params); + unset($update_globals); + unset($update_statics); + + if ($data->hasSource()) + { + $this->last->setSource($data->getSource()); + } + if (count($this->last->params) == 1 && !count($data->listParams())) + { + // if the function has no parameters, and 1 @param, add it to the list as optional, default value is description from @param + $pars = $this->last->listParams(); + $data->addParam($pars[0]['var'],$pars[0]['data']->getString()); + } + $data->setDocBlock($this->last); + $this->proceduralpages->addFunction($data); + $this->last = false; + } + + /** + * handles post-parsing of defines + * + * This function sets {@link $data}->clean to false to tell the + * phpDocumentor_IntermediateParser that a page-level DocBlock can't be + * found after this point on this page. It then sets the package to be the + * same as the page and adds itself to the {@link ProceduralPages} class + * @param integer $event Event number from {@link Parser.inc} + * @param parserDefine $data + */ + function handleDefine($event,$data) + { + if ($this->_lastDocBlockWasPageLevel) + { + addWarning(PDERROR_DOCBLOCK_CONFLICT, 'define', $data->getName()); + if (!$this->_oldPageLevel) + { + unset($this->last); + } + } + $this->_lastDocBlockWasPageLevel = false; + $this->data->clean = false; + if ($this->private_page) + { + unset($this->last); + return; + } + if (empty($this->last)) + { + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + } +// $this->last->setLineNumber($data->getLineNumber()); + if ($this->last->getKeyword('ignore')) + { + unset($this->last); + return; + } + + $this->last->overridePackage($this->category,$this->package,$this->subpackage,$data->getName(),'define'); + $data->setDocBlock($this->last); + $this->proceduralpages->addDefine($data); + $this->last = false; + } + + /** + * handles post-parsing of classes + * + * This function sets {@link $data}->clean to false to tell the + * phpDocumentor_IntermediateParser that a page-level DocBlock can't be + * found after this point on this page. It sets {@link $cur_class} to its + * name, and if an @ignore tag is found in the DocBlock, it sets + * {@link $private_class} to true, to prevent post-parsing of any of the + * class's vars or methods. Then it checks for the existence of a package + * page for the class's package + * @param integer $event Event number from {@link Parser.inc} + * @param parserClass $data + */ + function handleClass($event,$data) + { + global $_phpDocumentor_setting; + if ($data->isInterface()) + { + $objectType = 'interface'; + } + else + { + $objectType = 'class'; + } + if ($this->_lastDocBlockWasPageLevel) + { + if (!$this->_oldPageLevel) + { + addWarning(PDERROR_DOCBLOCK_GOES_CLASS, $data->getName()); + $doc = new parserDocBlock; + $doc->category = $this->category; + $doc->package = $this->package; + $doc->subpackage = $this->subpackage; + if ($_phpDocumentor_setting['sourcecode']) { + $doc->canSource(); + $doc->addFileSource($this->data->parent->path, $this->data->parent->source); + } + $this->data->setDocBlock($doc); + unset($doc); + if ($this->last) { + $this->last->cantSource(); + } + } + } + $this->_lastDocBlockWasPageLevel = false; + $this->data->clean = false; + if (empty($this->last)) + { + if ($this->undocumentedElementWarnings) + { + addWarning(PDERROR_UNDOCUMENTED_ELEMENT,'Class',$data->getName(),'Class'); + } + if (isset($this->db_template)) + { + // use the docblock template + $this->last = phpDocumentor_clone($this->db_template); + } + else + { + // we don't have a docblock, create an empty one to get rid of errors + $this->last = new parserDocblock(); + } + list($this->last->package, $this->last->subpackage) = $this->_guessPackage($this->data->parent->path, $this->data->parent->getSourceLocation('dummy')); + addWarning(PDERROR_NO_PACKAGE_TAG,$objectType,$data->getName(),$this->last->package); + } else + { + if (!$this->last->getExplicitPackage()) + { + list($this->last->package, $this->last->subpackage) = $this->_guessPackage($this->data->parent->path, $this->data->parent->getSourceLocation('dummy')); + addWarning(PDERROR_NO_PACKAGE_TAG,$objectType,$data->getName(),$this->last->package); + } else + { + if (isset($this->packagecategories[$this->package]) + && $this->packagecategories[$this->package] != $this->category) + addWarning(PDERROR_PACKAGECAT_SET,$this->package, + $this->packagecategories[$this->package], + $this->category); + $this->packagecategories[$this->package] = $this->category; + } + } + $this->last->updateModifiers($data->getModifiers()); +// $this->last->setLineNumber($data->getLineNumber()); + $data->setDocBlock($this->last); + $this->cur_class = $name = $data->getName(); + if ($this->last->getKeyword('ignore')) + { + $this->private_class = true; + unset($this->last); + return; + } + $data->path = $this->data->parent->path; + $this->classes->addClass($data); + $this->private_class = false; + if ($this->last->package) + { + $this->parsePackagePage($this->last->package, $this->data->parent->getPath()); + } + $this->last = false; + } + + /** + * handles post-parsing of procedural pages + * + * this event is called at the start of a new page, before the Parser knows + * whether the page will contain any procedural pages or not + * @param integer $event Event number from {@link Parser.inc} + * @param parserPage $data + */ + function handlePage($event,$data) + { + $type = 'page'; + $this->private_page = false; + $this->data = new parserData; + $data->category = $this->category = $GLOBALS['phpDocumentor_DefaultCategoryName']; + $this->package = $GLOBALS['phpDocumentor_DefaultPackageName']; + $this->subpackage = ''; + $this->proceduralpages->addPage($data); + $this->data->setParent($data); + $this->pages[$data->getPath()] = $this->data; + $this->classes->nextFile($data->getPath()); + $this->packageoutput = $data->getPackageOutput(); + } + + /** + * handles post-parsing of DocBlocks + * + * This function sets {@link $last} to the DocBlock represented by $data, to + * allow the next documentable element passed to + * phpDocumentor_IntermediateParser to link the DocBlock into its $docblock + * property. This function also checks for two special cases of DocBlocks: + * <ol> + * <li>First DocBlock in the file contains a @package tag</li> + * <li>First DocBlock in the file is immediately followed by another + * DocBlock</li> + * </ol> + * In both cases, the function extracts this tag and uses it as the + * page-level package. If the @package tag is in the DocBlock of an + * element (function, global variable, whatever) that isn't a page-level + * DocBlock, a warning will be raised to notify the author that a @package + * tag belongs in a page-level DocBlock. + * + * <b>New</b> in version 1.2.2, if the first DocBlock in a file contains + * a @package tag, it is a page-level DocBlock. + * + * If the DocBlock is page-level, it is processed with + * {@link _processPageLevelDocBlock} + * + * Finally, the function replaces the old parserPage in + * {@link parserData::$data}->parent with the new one containing information + * from the DocBlock by calling {@link addPage()}, and checks for + * package-level docs. + * @param integer $event Event number from {@link Parser.inc} + * @param parserDocBlock $data + */ + function handleDocBlock($event,$data) + { + $type = 'docblock'; + $data->postProcess(); + // Zend desc support + if ($tdesc = $data->getKeyword('desc')) + { + $data->setShortDesc($tdesc); + unset($data->tags['desc']); + } + $this->_lastDocBlockWasPageLevel = false; + // 1st docblock in file, check for @package + if ($this->data->isClean() && !isset($this->last)) + { + if ($data->getExplicitPackage()) + { + // new with 1.2.2: + // if the first docblock in a file + // contains a @package tag, then it is + // a page-level docblock + $this->_processPageLevelDocBlock($data); + $this->_lastDocBlockWasPageLevel = true; + $this->all_packages[$data->package] = 1; + $this->last = $data; + return; + } + $doc = new parserDocBlock; + $doc->category = $this->category; + $doc->package = $this->package; + $doc->subpackage = $this->subpackage; + $this->data->setDocBlock($doc); + $this->proceduralpages->addPagePackage($this->data->parent->getPath(),$this->package,$this->subpackage); + unset($doc); + } + // 2nd docblock in a row, and it's at the top of the file, page-level docblock + if ($this->lasttype == "docblock" && $this->data->isClean()) + { + $this->_processPageLevelDocBlock($this->last); + $this->_oldPageLevel = true; + $this->_lastDocBlockWasPageLevel = false; + } + $this->all_packages[$data->package] = 1; + $this->last = $data; + } + + /** + * Process a Page-level DocBlock + * + * First, it checks for an @ignore tag, + * and if found, calls {@link ProceduralPages::ignorePage()}. An @ignore + * tag in a page-level DocBlock will ignore all functions, defines, global + * variables, and includes. It will not ignore classes! The function next + * checks for an @access private, and if --parseprivate is off, performs the + * same actions as @ignore. + * Next, it checks for the @name tag, which is used to rename the page. + * This is also a PEAR compatibility issue, and may not be very useful in + * the long run. Documentation is best when it refers to real entities in + * the package, and not to aliases. + * @access private + */ + function _processPageLevelDocBlock($data) + { + global $_phpDocumentor_setting; + // can only have 1 package-level docblock, others are ignored + if (!$this->data->isClean()) + { + return; + } + $this->data->clean = false; + $this->data->explicitDocBlock(); + $data->canSource(); + if ($_phpDocumentor_setting['sourcecode']) + { + $data->addFileSource($this->data->parent->path, $this->data->parent->source); + } + if (!$data->getExplicitPackage()) + { + list($data->package,$data->subpackage) = $this->_guessPackage($this->data->parent->getPath(), $this->data->parent->getSourceLocation('dummy')); + addWarning(PDERROR_NO_PACKAGE_TAG,'file',$this->data->parent->getPath(),$this->last->package); + } + if (isset($this->packagecategories[$this->package]) + && $this->packagecategories[$this->package] != $data->category) + addWarning(PDERROR_PACKAGECAT_SET,$this->package, + $this->packagecategories[$this->package], + $data->category); + $this->packagecategories[$this->package] = $data->category; + $this->category = $this->data->parent->category = $data->category; + $this->packagecategories[$this->package] = $this->category; + $this->subpackage = $this->data->parent->subpackage = $data->subpackage; + $this->data->setDocBlock($data); + if ($data->getKeyword('ignore')) + { + $this->proceduralpages->ignorePage($this->data->parent); + $this->private_page = true; + unset($this->last); + $this->privatepages[$this->data->parent->getPath()] = $this->data; + unset($this->pages[$this->data->parent->getPath()]); + return; + } + $this->package = $this->data->parent->package = $data->package; + $this->subpackage = $this->data->parent->subpackage = $data->subpackage; + $this->proceduralpages->addPagePackage($this->data->parent->getPath(),$this->package,$this->subpackage); + if ($access = $data->getKeyword('access')) + { + if (is_object($access) && ($access->getString() == 'private') && (!$this->parsePrivate)) + { + $this->proceduralpages->ignorePage($this->data->parent); + $this->private_page = true; + unset($this->last); + $this->privatepages[$this->data->parent->getPath()] = $this->data; + unset($this->pages[$this->data->parent->getPath()]); + return; + } + } + if ($data->getKeyword('name')) + { + $a = $data->getKeyword('name'); + if (is_object($a)) $a = $a->value; + $this->data->parent->setFile($a); + $this->proceduralpages->setName($a); + } + $this->addPage($this->data->parent, $this->data->parent->getPath()); + if ($this->package) + { + $this->parsePackagePage($this->package, $this->data->parent->getPath()); + } + } + + /** + * Backward-compatibility only, use the new tutorials for more power + * @tutorial tutorials.pkg + * @param string package name of package file to parse + * @param string directory of file that contains package name + */ + function parsePackagePage($package, $path) + { + if (!isset($this->package_pages[$package])) + { + if (file_exists(dirname($path) . SMART_PATH_DELIMITER . $package . '.html')) + { + if ($this->quietMode === false) + { + phpDocumentor_out("Reading package-level file ".$package . '.html'); + flush(); + } + $fp = fopen(dirname($path) . SMART_PATH_DELIMITER . $package . '.html',"r"); + $ret = fread($fp,filesize(dirname($path) . SMART_PATH_DELIMITER . $package . '.html')); + fclose($fp); + unset($fp); + if ($this->quietMode === false) + { + phpDocumentor_out(" -- Parsing File\n"); + flush(); + } + $pageParser = new ppageParser; + $tempp = $this->package; + $lp = $this->last; + $pageParser->subscribe('*',$this); + $pageParser->parse($ret,false,$package); + $this->package = $tempp; + $this->last = $lp; + unset($tempp); + unset($pageParser); + } + } + } + + /** + * called via {@link Parser::parse()} and Parser's inherited method + * {@link Publisher::publishEvent()} + * + * $event is one of the PHPDOC constants from Parser.inc. If it is not + * PHPDOCUMENTOR_EVENT_NEWSTATE, then a function name is retrieved from the + * {@link $event_handlers} array and called to handle the $data + * @param integer $event event number from {@link Parser.inc} + * @param mixed $data if $event is {@link PHPDOCUMENTOR_EVENT_NEWSTATE}, $data is a {@link PHP_DOC_EVENT_END_PAGE} or {@link STATE_END_CLASS}, + * otherwise $data is either a {@link parserDocBlock}, {@link parserPage} or descendant of {@link parserElement} + * @global array we use 'sourcecode' to determine whether to highlight the source + * of the current file if it has no file-level docblock + */ + function HandleEvent($event,$data) + { + global $_phpDocumentor_setting; + global $phpDocumentor_DefaultPackageName, $phpDocumentor_DefaultCategoryName; + if (empty($this->packagecategories)) + $this->packagecategories[$phpDocumentor_DefaultPackageName] = $phpDocumentor_DefaultCategoryName; + if ($event == PHPDOCUMENTOR_EVENT_NEWSTATE) + { + if ($data == STATE_END_CLASS) + { + } elseif ($data == PHPDOCUMENTOR_EVENT_END_PAGE) + { + if (!$this->private_page) + { + $this->all_packages[$this->package] = 1; + if (!$this->data->hasExplicitDocBlock()) + { + $doc = $this->data->docblock; + if (!$this->data->docblock) + { + $doc = new parserDocBlock; + } + if ($_phpDocumentor_setting['sourcecode']) + { + $doc->canSource(); + $doc->addFileSource($this->data->parent->path, $this->data->parent->source); + } + list($doc->package,$doc->subpackage) = $this->_guessPackage($this->data->parent->getPath(), $this->data->parent->getSourceLocation('dummy')); + addWarning(PDERROR_NO_PAGE_LEVELDOCBLOCK,$this->data->parent->getPath()); + $this->data->setDocBlock($doc); + $this->proceduralpages->addPage($this->data->parent,$doc->package,$doc->subpackage); + } + $this->pages[$this->data->parent->getPath()] = $this->data; + } + $this->private_page = false; + $this->private_class = false; + if (isset($this->db_template)) + { + addWarning(PDERROR_DB_TEMPLATE_UNTERMINATED); + } + unset($this->db_template); + unset($this->last); + } elseif ($data == PHPDOCUMENTOR_EVENT_END_DOCBLOCK_TEMPLATE) + { + unset($this->db_template); + } + //echo $this->state_lookup[$data] . "\n"; + //echo $data."\n"; + } + else + { + if ($event == PHPDOCUMENTOR_EVENT_README_INSTALL_CHANGELOG) + { + $this->ric[$data[0]] = $data[1]; + return; + } + if ($event == PHPDOCUMENTOR_EVENT_DOCBLOCK_TEMPLATE) + { + $data->postProcess(); + $this->db_template = $data; + $this->_lastDocBlockWasPageLevel = false; + // 2nd docblock in a row, and it's at the top of the file, page-level docblock + if ($this->type == "docblock" && $this->data->isClean()) + { + // can only have 1 package-level docblock, others are ignored + $this->data->clean = false; + if ($this->last->getKeyword('ignore')) + { + $this->proceduralpages->ignorePage($this->data->parent); + $this->private_page = true; + unset($this->last); + $this->privatepages[$this->data->parent->getPath()] = $this->data; + unset($this->pages[$this->data->parent->getPath()]); + return; + } + $this->data->setDocBlock($this->last); + $this->package = $this->data->parent->package = $this->last->package; + $this->subpackage = $this->data->parent->subpackage = $this->last->subpackage; + $this->proceduralpages->addPagePackage($this->data->parent->getPath(),$this->package,$this->subpackage); + if ($access = $this->last->getKeyword('access')) + { + if (is_object($access) && ($access->getString() == 'private') && (!$this->parsePrivate)) + { + addWarning(PDERROR_PARSEPRIVATE, $this->data->parent->getPath()); + $this->proceduralpages->ignorePage($this->data->parent); + $this->private_page = true; + unset($this->last); + $this->privatepages[$this->data->parent->getPath()] = $this->data; + unset($this->pages[$this->data->parent->getPath()]); + return; + } + } + if ($this->last->getKeyword('name')) + { + $a = $this->last->getKeyword('name'); + if (is_object($a)) $a = $a->value; + $this->data->parent->setFile($a); + $this->proceduralpages->setName($a); + } + $this->addPage($this->data->parent, $this->data->parent->getPath()); + if ($this->package) + { + $this->parsePackagePage($this->package, $this->data->parent->getPath()); + } + } + unset($this->last); + } else + { + $this->lasttype = $this->type; + $type = $data->getType(); +// fancy_debug($type,$data); + if (($type != 'page') && ($type != 'docblock') && ($type != 'packagepage') && ($type != 'tutorial')) + { + $data->setFile($this->data->parent->getFile()); + } + $this->type = $type; + //echo $type . "\n"; + + if (isset($this->event_handlers[$type])) + { + $handle = $this->event_handlers[$type]; + $this->$handle($event,$data); + } + } + } + } + + /** + * Replaces the {@link parserPage} represented by $this->pages[$path] with + * $page + * + * Called by {@link addPageIfNecessary(), handleDocBlock()} and + * {@link ProceduralPages::setupPages()}, this method first checks to see if + * the page has been added. If not, it assumes that the page has either + * been @ignored or set with @access private with --parseprivate off, and + * returns {@link addPrivatePage()}. Otherwise, it sets the pages[$path] to + * be the parserPage $page and sets the package and subpackage to that of + * $page + * @see $pages + * @param parserPage + * @param string full path to the file + */ + function addPage($page, $path) + { + if (!isset($this->pages[$path])) return $this->addPrivatePage($page, $path); + $this->pages[$path]->setParent($page); + if ($page->package != $GLOBALS['phpDocumentor_DefaultPackageName']) + { + if (!$this->pages[$path]->docblock) + { + $docblock = new parserDocBlock; + $docblock->package = $page->package; + $docblock->subpackage = $page->subpackage; + $this->pages[$path]->docblock = $docblock; + } else + { + $this->pages[$path]->docblock->package = $page->package; + $this->pages[$path]->docblock->subpackage = $page->subpackage; + } + } + } + + /** + * add a new {@link parserPage} to the $pages array if none is found + * + * This method is used when a page has been @ignored or marked with @access + * private, and a public class is in the page (a class with no @access + * private in its DocBlock). The method first creates a new page in the + * {@link $pages} array and then copies path information, and calls + * {@link addPage()} to set up packages + * @param string full path of page + */ + function addPageIfNecessary($path, &$class) + { + global $_phpDocumentor_setting; + if (!$this->parsePrivate) + { + if (!isset($this->pages[$path])) + { + $this->pages[$path] = new parserData; + $this->pages[$path]->docblock = new parserDocBlock; + $this->pages[$path]->docblock->package = $this->privatepages[$path]->docblock->package; + $this->pages[$path]->docblock->subpackage = $this->privatepages[$path]->docblock->subpackage; + $par = $this->privatepages[$path]->parent; + $this->pages[$path]->setParent($par); + $this->proceduralpages->addPage($par); + } + } + if (!empty($_phpDocumentor_setting['packageoutput'])) + $packages = explode(',',$_phpDocumentor_setting['packageoutput']); + if (!empty($_phpDocumentor_setting['packageoutput']) && + $this->pages[$path]->parent->package != $class->docblock->package && + !in_array($this->pages[$path]->parent->package,$packages)) + { + $this->pages[$path]->parent->package = $class->docblock->package; + $this->addPage($this->pages[$path]->parent, $path); + $this->proceduralpages->addPage($this->pages[$path]->parent); + } + } + + /** + * Adds a {@link parserPage} element to the {@link parserData} element in + * $this->privatepages[$path] + * + * Performs a similar function to addPage, but adds to the + * {@link $privatePages} array + * @param parserPage $page + * @param string $path full path to the page + * @see addPage() + */ + function addPrivatePage($page, $path) + { + /* + * if privatepages is still empty, + * we need to initialize it with an empty + * path=>ParserData element, so that it has + * a top-level element... otherwise the setParent() call + * below will crap out. + */ + if (count($this->privatepages) == 0) { + $this->privatepages[$path] = new ParserData(); + } + $this->privatepages[$path]->setParent($page); + if (isset($page->package) && $page->package != $GLOBALS['phpDocumentor_DefaultPackageName']) + { + if (!$this->privatepages[$path]->docblock) + { + $docblock = new parserDocBlock; + $docblock->package = $page->package; + $docblock->subpackage = $page->subpackage; + $this->privatepages[$path]->docblock = $docblock; + } else + { + $this->privatepages[$path]->docblock->package = $page->package; + $this->privatepages[$path]->docblock->subpackage = $page->subpackage; + } + } + } + + /** + * adds a processed descendant of {@link parserElement} to the {@link $pages} + * array or {@link $privatepages} array + * + * This function expects the page to exist in either $pages or $privatepages. It calls the + * {@link parserData::addElement()} method to add $element to the page. + * @param parserElement $element this will actually be a descendant of parserElement + * @param string $path + */ + function addElementToPage($element, $path) + { + if (isset($this->privatepages[$path])) + { + if (isset($this->pages[$path])) + { + if ($element->type == 'class' || $element->type == 'method' + || $element->type == 'var' || $element->type == 'const') + { + $this->pages[$path]->addElement($element); + } else + $this->privatepages[$path]->addElement($element); + } else + $this->privatepages[$path]->addElement($element); + } else + { + if (isset($this->pages[$path])) + { + $this->pages[$path]->addElement($element); + } + } + } + + /** + * Add all the @uses tags from $element to the $uses array so that @usedby + * virtual tags can be added + * @uses parserUsesTag::getSeeElement() used to initialize {@link $uses} + * @uses parserUsesTag::getDescription() used to initialize {@link $uses} + * @param parserElement descendant of parserElement + * @param string full path to the file + */ + function addUses($element, $path) + { + if (isset($element->type) && $element->type == 'page') + { + $element = $this->pages[$element->path]; + } + if (!$this->parsePrivate && isset($element->docblock->hasaccess) && $element->docblock->hasaccess) + { + $a = $element->docblock->getKeyword('access'); + if (is_object($a) && $a->getString() == 'private') return; + } + if (isset($this->privatepages[$path])) + { + if (isset($this->pages[$path])) + { + $uses = $element->docblock->getKeyword('uses'); + if ($uses) + { + if (!is_array($uses)) $uses = array($uses); + foreach($uses as $use) + { + if (!is_object($use)) continue; + $el = $use->getSeeElement(); + $description = $use->getDescription(); + $this->uses[$el][] = array($element, $description); + } + } + } + } else + { + if (isset($this->pages[$path])) + { + $uses = $element->docblock->getKeyword('uses'); + if ($uses) + { + if (!is_array($uses)) $uses = array($uses); + foreach($uses as $use) + { + if (!is_object($use)) continue; + $el = $use->getSeeElement(); + $description = $use->getDescription(); + $this->uses[$el][] = array($element, $description); + } + } + } + } + } + + /** + * Add a {@link parserUsedByTag} link to every element referred to by @uses + * @param Converter temporary converter used to retrieve abstract links + * @uses phpDocumentor_IntermediateParser::addUses() indirectly, as + * addUses() sets up $uses, which is iterated over here + * @uses $pages sets up all @usedby tags from here + * @access private + */ + function _setupUsesList(&$converter) + { + ob_start(); + $converter->_createPkgElements($this->pages); + ob_end_clean(); + ksort($this->uses); + foreach($this->uses as $link => $elements) + { + foreach($elements as $element) + { + if ($element[0]->type == 'method' || $element[0]->type == 'var' || + $element[0]->type == 'const') + { + $converter->class = $element[0]->getClass(); + } + if ($element[0]->type == 'class') + { + $converter->class = $element[0]->getName(); + } + $reallink = $converter->getLink($link,$element[0]->docblock->package); + if (is_object($reallink)) + { + // add a used by tag to the docblock of the destination + switch(phpDocumentor_get_class($reallink)) + { + case 'pagelink' : + $this->pages[$reallink->path]->docblock->addUsedBy( + $element[0]->getLink($converter, false, true), + $element[1]); + break; + case 'functionlink' : + case 'definelink' : + case 'globallink' : + if (isset($this->pages[$reallink->path])) + { + for ($i=0; + $i<count($this->pages[$reallink->path]->elements); + $i++) { + if ($this->pages[$reallink->path]->elements[$i]->type == + str_replace('link', '', + phpDocumentor_get_class($reallink)) && + $this->pages[$reallink->path]-> + elements[$i]->getName() + == $reallink->name) { + $this->pages[$reallink->path]->elements[$i]-> + docblock->addUsedBy( + $element[0]->getLink($converter,false,true), + $element[1]); +// debug('added @usedby to '.str_replace('link','',phpDocumentor_get_class($reallink)).' '.$reallink->name); + } + } + } + break; + case 'classlink' : + case 'methodlink' : + case 'varlink' : + case 'constlink' : + if (isset($this->pages[$reallink->path])) + { + for ($i=0;$i<count($this->pages[$reallink->path]->classelements);$i++) + { + if ($this->pages[$reallink->path]->classelements[$i]->type == + str_replace('link','',phpDocumentor_get_class($reallink)) && + $this->pages[$reallink->path]->classelements[$i]->getName() == $reallink->name && + (!isset($reallink->class) || + $this->pages[$reallink->path]->classelements[$i]->getClass() == $reallink->class)) + { + $this->pages[$reallink->path]->classelements[$i]->docblock->addUsedBy($element[0]->getLink($converter,false,true), $element[1]); +// debug('added @usedby to '.str_replace('link','',phpDocumentor_get_class($reallink)).' '.$reallink->name); + } + } + } + break; + } + } + } + } + } + + /** + * Interface to the Converter + * + * This function simply passes {@link $pages} and {@link package_pages} to + * the walk() method, and then calls the Output() method. Note that + * Output() is not required to do anything, and in fact doesn't in + * HTMLframesConverter. + * @uses Converter::walk() passes {@link $pages} and {@link $package_pages} + * @uses Converter::Output() + */ + function Convert($title, $converter) + { + $converter->walk($this->pages, $this->package_pages); + $converter->Output($title); + $converter->cleanup(); + } + + /** + * Clean up classes + * + * {@source} + * @access private + * @uses Classes::Inherit() passes $this + */ + function fixClasses() + { + $this->classes->Inherit($this); + } + + /** + * Clean up Procedural Pages + * {@source} + * @access private + * @uses ProceduralPages::setupPages() passes $this + */ + function fixProcPages() + { + $this->proceduralpages->setupPages($this); + } + + /** + * If the parent class of $class is in a different package, adds it to the + * {@link $package_parents} array + * @param parserClass &$class + */ + function addPackageParent(&$class) + { + if (!is_array($class->parent)) return; + $par = $this->classes->getClass($class->parent[1], $class->parent[0]); + if ($class->docblock->package == $par->docblock->package) return; + $this->package_parents[$class->docblock->package] = $par->docblock->package; + if (!isset($this->package_parents[$par->docblock->package]) || !$this->package_parents[$par->docblock->package]) $this->package_parents[$par->docblock->package] = false; + } + + /** + * Add a converter name to use to the list of converters + * + * Sets up the {@link $converters} array. + * {@internal + * First, the Converter's file is included, and then, if successful, + * the converter classname is tested for existance. If all is good, + * then the templates are added to the list of converters/templates to use}} + * @param string $output output format (HTML, PDF, XML). Must be all caps + * @param string $name Converter name (frames, for example, is the name of + * HTMLframesConverter) + * @param string $template template to use, should be a relative path to the + * templates dir (like DOM/default) + */ + function addConverter($output,$name,$template) + { + if ($this->templateBase) { + $templateBase = str_replace('\\','/', $this->templateBase) . '/Converters'; + } else { + if ('@PEAR-DIR@' != '@'.'PEAR-DIR@') { + $templateBase = 'PhpDocumentor/phpDocumentor/Converters'; + } else { + $templateBase = str_replace('\\','/',$GLOBALS['_phpDocumentor_install_dir']) . '/phpDocumentor/Converters'; + } + } + if (strpos($name,PATH_DELIMITER)) + { + // include the parent template + $parent = explode(PATH_DELIMITER,$name); + $parent = $parent[0]; + if (!class_exists($output . $parent . 'Converter')) { + $filename = $templateBase . '/' . $output . '/' . $parent . '/' . $output + . $parent . 'Converter.inc'; + if (Io::isIncludeable($filename)) + { + include_once($filename); + } + } + if (!class_exists($output . $parent . 'Converter')) + { + addError(PDERROR_CONVERTER_NOT_FOUND,"parent Converter ".$output . $parent . "Converter of child Converter ".$output . str_replace(PATH_DELIMITER,'',$name) . "Converter"); + } + } + $filename = $templateBase . + PATH_DELIMITER . $output . PATH_DELIMITER . $name . PATH_DELIMITER . $output . + str_replace(PATH_DELIMITER, '', $name) . "Converter" . ".inc"; + if (Io::isIncludeable($filename)) + { + include_once($filename); + } + if (class_exists($output . str_replace(PATH_DELIMITER,'',$name) . 'Converter')) + { + $this->converters[$output][$output . str_replace(PATH_DELIMITER,'',$name) . "Converter"][] = $template; + } else + { + addError(PDERROR_CONVERTER_NOT_FOUND,$output . str_replace(PATH_DELIMITER,'',$name) . "Converter"); + } + } + + /** + * does a natural case sort on two {@link parserElement} descendants + * + * @param mixed $a + * @param mixed $b + * @return int + * @see generateElementIndex() + */ + function elementCmp ($a, $b) + { + return strnatcasecmp($a->getName(), $b->getName()); + } + + /** + * does a natural case sort on two class elements (either + * {@link parserClass, parserMethod} or {@link parserVar} + * + * @param mixed $a + * @param mixed $b + * @return int + * @see generateElementIndex() + */ + function ClasselementCmp ($a, $b) + { + if (phpDocumentor_get_class($a) == 'parserclass') $atest = $a->name; else $atest = $a->class; + if (phpDocumentor_get_class($b) == 'parserclass') $btest = $b->name; else $btest = $b->class; + + if(($c = strnatcasecmp($atest, $btest)) != 0) return $c; + if (phpDocumentor_get_class($a) != 'parserclass') $atest .= $a->name; + if (phpDocumentor_get_class($b) != 'parserclass') $btest .= $b->name; + if (phpDocumentor_get_class($a) == 'parsermethod' && phpDocumentor_get_class($b) == 'parsermethod') + { + if ($a->isConstructor) return -1; + if ($b->isConstructor) return 1; + if ($a->isDestructor) return -1; + if ($b->isDestructor) return 1; + } + return strnatcasecmp($atest,$btest); + } + + /** + * call this method once parsing has completed. + * + * This method calls the private methods fixClasses and fixProcPages, both + * of which adjust inheritance and package information based on complicated + * post-parsing rules described in {@link ProceduralPages::setupPages()} + * and {@link Classes::Inherit()}. Then, it sorts elements of the $pages + * array and calls Convert for each Converter in the $converters array + * @see $converters + * @see $pages + * @see Convert() + */ + function Output ($title = "Generated Documentation") + { + $GLOBALS['phpDocumentor_errors']->curfile = false; + $this->fixClasses(); + $this->fixProcPages(); +// var_dump($this->uses); +// exit; + phpDocumentor_out("\nSorting page elements..."); + flush(); + uasort($this->pages,'pagesort'); + foreach($this->pages as $i => $page) + { + usort($this->pages[$i]->elements,array($this,'elementCmp')); + usort($this->pages[$i]->classelements,array($this,'ClasselementCmp')); + } + phpDocumentor_out("done\n"); + flush(); + $complicatedout = false; + if (is_array($this->converters)) + { + if (count($this->converters) > 1) + { + $complicatedout = true; + } + phpDocumentor_out("Formatting @uses list..."); + flush(); + $a = new __dummyConverter($this->all_packages, $this->package_parents, $this->classes, $this->proceduralpages, $this->packageoutput, $this->parsePrivate, $this->quietMode, $this->targetDir , '', $this->title); + $this->_setupUsesList($a); + unset($a); + phpDocumentor_out("done\n\n"); + flush(); + foreach($this->converters as $converter => $blah) + { + if (is_array($blah)) + { + if (count($blah) > 1) + { + $complicatedout = true; + } + foreach($blah as $converter => $templates) + { + foreach($templates as $template) + { + $extraout = ''; + if ($complicatedout) + { + $extraout = SMART_PATH_DELIMITER . $converter; + } + if (count($templates) > 1) + { + $extraout .= SMART_PATH_DELIMITER . str_replace(PATH_DELIMITER, SMART_PATH_DELIMITER, substr($template,0,strlen($template) - 1)); + } + $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); + if (isset($this->templateBase)) + { + $a->setTemplateBase($this->templateBase, $template); + } + $a->ric = $this->ric; + $a->packagecategories = $this->packagecategories; + if (isset($this->tutorials)) $a->setTutorials($this->tutorials); + $this->Convert($title, $a); + unset($a); + } + } + } + } + } else + { + addErrorDie(PDERROR_NO_CONVERTERS); + } + } + + /** + * Sets the output directory + * + * @param string $dir the output directory + */ + function setTargetDir($dir) + { + $this->targetDir = $dir; + } + + /** + * Sets the template base directory + * + * @param string $dir the template base directory + * @tutorial phpDocumentor.howto.pkg#using.command-line.templatebase + */ + function setTemplateBase($dir) + { + $this->templateBase = $dir; + } + + /** + * set parsing information output mode (quiet or verbose) + * + * If set to false, no parsing information (parsing /php/file/thisfile.php, + * Converting etc.) will be displayed. + * Useful for cron jobs + * @param bool $quietMode + */ + function setQuietMode($quietMode) + { + $this->quietMode = $quietMode; + } + + /** + * show warnings for undocumented elements + * + * If set to false, no warnings will be shown for undocumented elements. + * Useful for identifying classes and methods that haven't yet been documented. + * @param bool $undocumentedElementWarnings + */ + function setUndocumentedElementWarningsMode($undocumentedElementWarnings) + { + $this->undocumentedElementWarnings = $undocumentedElementWarnings; + } + + /** + * set display of elements marked with @access private + * + * If set to true, elements will be displayed + * @param bool $parse + */ + function setParsePrivate($parse) + { + $this->parsePrivate = $parse; + } +} + +/** @access private */ +function pagesort($a, $b) +{ + return strnatcasecmp($a->parent->file,$b->parent->file); +} +?> |