<?php // // +------------------------------------------------------------------------+ // | phpDocumentor | // +------------------------------------------------------------------------+ // | Copyright (c) 2000-2003 Joshua Eichorn, Gregory Beaver | // | Email jeichorn@phpdoc.org, cellog@phpdoc.org | // | Web http://www.phpdoc.org | // | Mirror http://phpdocu.sourceforge.net/ | // | PEAR http://pear.php.net/package-info.php?pacid=137 | // +------------------------------------------------------------------------+ // | This source file is subject to version 3.00 of the PHP License, | // | that is available at http://www.php.net/license/3_0.txt. | // | If you did not receive a copy of the PHP license and are unable to | // | obtain it through the world-wide-web, please send a note to | // | license@php.net so we can mail you a copy immediately. | // +------------------------------------------------------------------------+ // /** * Base class for all output converters. * @package Converters * @author Greg Beaver <cellog@users.sourceforge.net> * @since 1.0rc1 * @version $Id: Converter.inc,v 1.5 2005/11/28 07:27:59 cellog Exp $ */ /** * Smarty template files */ include_once("phpDocumentor/Smarty-2.6.0/libs/Smarty.class.php"); /** * Base class for all output converters. * * The Converter marks the final stage in phpDocumentor. phpDocumentor works * in this order: * * <pre>Parsing => Intermediate Parsing organization => Conversion to output</pre> * * A Converter takes output from the {@link phpDocumentor_IntermediateParser} and * converts it to output. With version 1.2, phpDocumentor includes a variety * of output converters: * <ul> * <li>{@link HTMLframesConverter}</li> * <li>{@link HTMLSmartyConverter}</li> * <li>{@link PDFdefaultConverter}</li> * <li>{@link CHMdefaultConverter}</li> * <li>{@link CSVdia2codeConverter}</li> * <li>{@link XMLDocBookConverter}</li> * </ul> * {@internal * The converter takes output directly from {@link phpDocumentor_IntermediateParser} * and using {@link walk()} or {@link walk_everything} (depending on the value of * {@link $sort_absolutely_everything}) it "walks" over an array of phpDocumentor elements.}} * * @package Converters * @abstract * @author Greg Beaver <cellog@users.sourceforge.net> * @since 1.0rc1 * @version $Id: Converter.inc,v 1.5 2005/11/28 07:27:59 cellog Exp $ */ class Converter { /** * output format of this converter * * in Child converters, this will match the first part of the -o command-line * as in -o HTML:frames:default "HTML" * @tutorial phpDocumentor.howto.pkg#using.command-line.output * @var string */ var $outputformat = 'Generic'; /** * package name currently being converted * @var string */ var $package = 'default'; /** * subpackage name currently being converted * @var string */ var $subpackage = ''; /** * set to a classname if currently parsing a class, false if not * @var string|false */ var $class = false; /**#@+ * @access private */ /** * the workhorse of linking. * * This array is an array of link objects of format: * [package][subpackage][eltype][elname] = descendant of {@link abstractLink} * eltype can be page|function|define|class|method|var * if eltype is method or var, the array format is: * [package][subpackage][eltype][class][elname] * @var array * @see functionLink, pageLink, classLink, defineLink, methodLink, varLink, globalLink */ var $links = array(); /** * the workhorse of linking, with allowance for support of multiple * elements in different files. * * This array is an array of link objects of format: * [package][subpackage][eltype][file][elname] = descendant of {@link abstractLink} * eltype can be function|define|class|method|var * if eltype is method or var, the array format is: * [package][subpackage][eltype][file][class][elname] * @var array * @see functionLink, pageLink, classLink, defineLink, methodLink, varLink, globalLink */ var $linkswithfile = array(); /**#@-*/ /** * set to value of -po commandline * @tutorial phpDocumentor.howto.pkg#using.command-line.packageoutput * @var mixed */ var $package_output; /** * name of current page being converted * @var string */ var $page; /** * path of current page being converted * @var string */ var $path; /** * template for the procedural page currently being processed * @var Smarty */ var $page_data; /** * template for the class currently being processed * @var Smarty */ var $class_data; /** * current procedural page being processed * @var parserPage */ var $curpage; /** * alphabetical index of all elements sorted by package, subpackage, page, * and class. * @var array Format: array(package => array(subpackage => array('page'|'class' => array(path|classname => array(element, element,...))))) * @uses $sort_absolutely_everything if true, then $package_elements is used, * otherwise, the {@link ParserData::$classelements} and * {@link ParserData::$pageelements} variables are used */ var $package_elements = array(); /** * alphabetical index of all elements * * @var array Format: array(first letter of element name => array({@link parserElement} or {@link parserPage},...)) * @see formatIndex(), HTMLframesConverter::formatIndex() */ var $elements = array(); /** * alphabetized index of procedural pages by package * * @see $leftindex * @var array Format: array(package => array(subpackage => array({@link pageLink} 1,{@link pageLink} 2,...) */ var $page_elements = array(); /** * alphabetized index of defines by package * * @see $leftindex * @var array Format: array(package => array(subpackage => array({@link defineLink} 1,{@link defineLink} 2,...) */ var $define_elements = array(); /** * alphabetized index of classes by package * * @see $leftindex * @var array Format: array(package => array(subpackage => array({@link classLink} 1,{@link classLink} 2,...) */ var $class_elements = array(); /** * alphabetized index of global variables by package * * @see $leftindex * @var array Format: array(package => array(subpackage => array({@link globalLink} 1,{@link globalLink} 2,...) */ var $global_elements = array(); /** * alphabetized index of functions by package * * @see $leftindex * @var array Format: array(package => array(subpackage => array({@link functionLink} 1,{@link functionLink} 2,...) */ var $function_elements = array(); /** * alphabetical index of all elements, indexed by package/subpackage * * @var array Format: array(first letter of element name => array({@link parserElement} or {@link parserPage},...)) * @see formatPkgIndex(), HTMLframesConverter::formatPkgIndex() */ var $pkg_elements = array(); /** * alphabetical index of all elements on a page by package/subpackage * * The page itself has a link under ###main * @var array Format: array(package => array(subpackage => array(path => array({@link abstractLink} descendant 1, ...))) * @see formatLeftIndex() */ var $page_contents = array(); /** * This determines whether the {@link $page_contents} array should be sorted by element type as well as alphabetically by name * @see sortPageContentsByElementType() * @var boolean */ var $sort_page_contents_by_type = false; /** * This is used if the content must be passed in the order it should be read, i.e. by package, procedural then classes * * This fixes bug 637921, and is used by {@link PDFdefaultConverter} */ var $sort_absolutely_everything = false; /** * alphabetical index of all methods and vars in a class by package/subpackage * * The class itself has a link under ###main * @var array * Format:<pre> * array(package => * array(subpackage => * array(path => * array(class => * array({@link abstractLink} descendant 1, ... * ) * ) * ) * )</pre> * @see formatLeftIndex() */ var $class_contents = array(); /** * controls processing of elements marked private with @access private * * defaults to false. Set with command-line --parseprivate or -pp * @var bool */ var $parseprivate; /** * controls display of progress information while parsing. * * defaults to false. Set to true for cron jobs or other situations where no visual output is necessary * @var bool */ var $quietmode; /** * directory that output is sent to. -t command-line sets this. * @tutorial phpDocumentor.howto.pkg#using.command-line.target */ var $targetDir = ''; /** * Directory that the template is in, relative to phpDocumentor root directory * @var string */ var $templateDir = ''; /** * Directory that the smarty templates are in * @var string */ var $smarty_dir = ''; /** * Name of the template, from last part of -o * @tutorial phpDocumentor.howto.pkg#using.command-line.output * @var string */ var $templateName = ''; /** * full path of the current file being converted */ var $curfile; /** * All class information, organized by path, and by package * @var Classes */ var $classes; /** * Hierarchy of packages * * Every package that contains classes may have parent or child classes * in other packages. In other words, this code is legal: * * <code> * /** * * @package one * * / * class one {} * * /** * * @package two * * / * class two extends one {} * </code> * * In this case, package one is a parent of package two * @var array * @see phpDocumentor_IntermediateParser::$package_parents */ var $package_parents; /** * Packages associated with categories * * Used by the XML:DocBook/peardoc2 converter, and available to others, to * group many packages into categories * @see phpDocumentor_IntermediateParser::$packagecategories * @var array */ var $packagecategories; /** * All packages encountered in parsing * @var array * @see phpDocumentor_IntermediateParser::$all_packages */ var $all_packages; /** * A list of files that have had source code generated * @var array */ var $sourcePaths = array(); /** * Controls which of the one-element-only indexes are generated. * * Generation of these indexes for large packages is time-consuming. This is an optimization feature. An * example of how to use this is in {@link HTMLframesConverter::$leftindex}, and in {@link HTMLframesConverter::formatLeftIndex()}. * These indexes are intended for use as navigational aids through documentation, but can be used for anything by converters. * @see $class_elements, $page_elements, $function_elements, $define_elements, $global_elements * @see formatLeftIndex() * @var array */ var $leftindex = array('classes' => true, 'pages' => true, 'functions' => true, 'defines' => true, 'globals' => true); /** @access private */ var $killclass = false; /** * @var string * @see phpDocumentor_IntermediateParser::$title */ var $title = 'Generated Documentation'; /** * Options for each template, parsed from the options.ini file in the template base directory * @tutorial phpDocumentor/tutorials.pkg#conversion.ppage * @var array */ var $template_options; /** * Tutorials and Extended Documentation parsed from a tutorials/package[/subpackage] directory * @tutorial tutorials.pkg * @access private */ var $tutorials = array(); /** * tree-format structure of tutorials and their child tutorials, if any * @var array * @access private */ var $tutorial_tree = false; /** * list of tutorials that have already been processed. Used by @link _setupTutorialTree() * @var array * @access private */ var $processed_tutorials; /** * List of all @todo tags and a link to the element with the @todo * * Format: array(package => array(link to element, array(todo {@link parserTag},...)),...) * @tutorial tags.todo.pkg * @var array */ var $todoList = array(); /** * Initialize Converter data structures * @param array {@link $all_packages} value * @param array {@link $package_parents} value * @param Classes {@link $classes} value * @param ProceduralPages {@link $proceduralpages} value * @param array {@link $package_output} value * @param boolean {@link $parseprivate} value * @param boolean {@link $quietmode} value * @param string {@link $targetDir} value * @param string {@link $templateDir} value * @param string (@link $title} value */ function Converter(&$allp, &$packp, &$classes, &$procpages, $po, $pp, $qm, $targetDir, $template, $title) { $this->all_packages = $allp; $this->package_parents = $packp; $this->package = $GLOBALS['phpDocumentor_DefaultPackageName']; $this->proceduralpages = &$procpages; $this->package_output = $po; if (is_array($po)) { $a = $po[0]; $this->all_packages = array_flip($po); $this->all_packages[$a] = 1; } $this->parseprivate = $pp; $this->quietmode = $qm; $this->classes = &$classes; $this->roots = $classes->getRoots(); $this->title = $title; $this->setTemplateDir($template); $this->setTargetdir($targetDir); } /** * Called by IntermediateParser after creation * @access private */ function setTutorials($tutorials) { $this->tutorials = $tutorials; } /** * @param pkg|cls|proc the tutorial type to search for * @param tutorial name * @param string package name * @param string subpackage name, if any * @return false|parserTutorial if the tutorial exists, return it */ function hasTutorial($type, $name, $package, $subpackage = '') { if (isset($this->tutorials[$package][$subpackage][$type][$name . '.' . $type])) return $this->tutorials[$package][$subpackage][$type][$name . '.' . $type]; return false; } /** * Called by {@link walk()} while converting, when the last class element * has been parsed. * * A Converter can use this method in any way it pleases. HTMLframesConverter * uses it to complete the template for the class and to output its * documentation * @see HTMLframesConverter::endClass() * @abstract */ function endClass() { } /** * Called by {@link walk()} while converting, when the last procedural page * element has been parsed. * * A Converter can use this method in any way it pleases. HTMLframesConverter * uses it to complete the template for the procedural page and to output its * documentation * @see HTMLframesConverter::endClass() * @abstract */ function endPage() { } /** * Called by {@link walk()} while converting. * * This method is intended to be the place that {@link $pkg_elements} is * formatted for output. * @see HTMLframesConverter::formatPkgIndex() * @abstract */ function formatPkgIndex() { } /** * Called by {@link walk()} while converting. * * This method is intended to be the place that {@link $elements} is * formatted for output. * @see HTMLframesConverter::formatIndex() * @abstract */ function formatIndex() { } /** * Called by {@link walk()} while converting. * * This method is intended to be the place that any of * {@link $class_elements, $function_elements, $page_elements}, * {@link $define_elements}, and {@link $global_elements} is formatted for * output, depending on the value of {@link $leftindex} * @see HTMLframesConverter::formatLeftIndex() * @abstract */ function formatLeftIndex() { } /** * Called by {@link parserSourceInlineTag::stringConvert()} to allow * converters to format the source code the way they'd like. * * default returns it unchanged (html with xhtml tags) * @param string output from highlight_string() - use this function to * reformat the returned data for Converter-specific output * @return string * @deprecated in favor of tokenizer-based highlighting. This will be * removed for 2.0 */ function unmangle($sourcecode) { return $sourcecode; } /** * Initialize highlight caching */ function startHighlight() { $this->_highlightCache = array(false, false); $this->_appendHighlight = ''; } function getHighlightState() { return $this->_highlightCache; } function _setHighlightCache($type, $token) { $test = ($this->_highlightCache[0] === $type && $this->_highlightCache[1] == $token); if (!$test) { $this->_appendHighlight = $this->flushHighlightCache(); } else { $this->_appendHighlight = ''; } $this->_highlightCache = array($type, $token); return $test; } /** * Return the close text for the current token * @return string */ function flushHighlightCache() { $hc = $this->_highlightCache; $this->_highlightCache = array(false, false); if ($hc[0]) { if (!isset($this->template_options[$hc[0]]['/'.$hc[1]])) { return ''; } return $this->template_options[$hc[0]]['/'.$hc[1]]; } return ''; } /** * Used to allow converters to format the source code the way they'd like. * * default returns it unchanged. Mainly used by the {@link HighlightParser} * {@internal * The method takes information from options.ini, the template options * file, specifically the [highlightSourceTokens] and [highlightSource] * sections, and uses them to enclose tokens. * * {@source}}} * @param integer token value from {@link PHP_MANUAL#tokenizer tokenizer constants} * @param string contents of token * @param boolean whether the contents are preformatted or need modification * @return string */ function highlightSource($token, $word, $preformatted = false) { if ($token !== false) { if (!$preformatted) $word = $this->postProcess($word); if (isset($this->template_options['highlightSourceTokens'][token_name($token)])) { if ($this->_setHighlightCache('highlightSourceTokens', token_name($token))) { return $word; } $e = $this->_appendHighlight; return $e . $this->template_options['highlightSourceTokens'][token_name($token)] . $word; } else { $this->_setHighlightCache(false, false); $e = $this->_appendHighlight; return $e . $word; } } else { if (isset($this->template_options['highlightSource'][$word])) { $newword = ($preformatted ? $word : $this->postProcess($word)); if ($this->_setHighlightCache('highlightSource', $word)) { return $newword; } $e = $this->_appendHighlight; return $e . $this->template_options['highlightSource'][$word] . $newword; } else { $this->_setHighlightCache(false, false); $e = $this->_appendHighlight; return $e . ($preformatted ? $word : $this->postProcess($word)); } } } /** * Used to allow converters to format the source code of DocBlocks the way * they'd like. * * default returns it unchanged. Mainly used by the {@link HighlightParser} * {@internal * The method takes information from options.ini, the template options * file, specifically the [highlightDocBlockSourceTokens] section, and uses * it to enclose tokens. * * {@source}}} * @param string name of docblock token type * @param string contents of token * @param boolean whether the contents are preformatted or need modification * @return string */ function highlightDocBlockSource($token, $word, $preformatted = false) { if (empty($word)) { $this->_setHighlightCache(false, false); $e = $this->_appendHighlight; return $e . $word; } if (isset($this->template_options['highlightDocBlockSourceTokens'][$token])) { if (!$preformatted) $word = $this->postProcess($word); if ($this->_setHighlightCache('highlightDocBlockSourceTokens', $token)) { return $word; } $e = $this->_appendHighlight; return $e . $this->template_options['highlightDocBlockSourceTokens'][$token] . $word; } else { $this->_setHighlightCache(false, false); $e = $this->_appendHighlight; return $e . ($preformatted ? $word : $this->postProcess($word)); } } /** * Used to allow converters to format the source code of DocBlocks the way * they'd like. * * default returns it unchanged. Mainly used by the {@link HighlightParser} * {@internal * The method takes information from options.ini, the template options * file, specifically the [highlightDocBlockSourceTokens] section, and uses * it to enclose tokens. * * {@source}}} * @param string name of docblock token type * @param string contents of token * @param boolean whether the contents are preformatted or need modification * @return string */ function highlightTutorialSource($token, $word, $preformatted = false) { if (empty($word)) { $this->_setHighlightCache(false, false); $e = $this->_appendHighlight; return $e . $word; } if (isset($this->template_options['highlightTutorialSourceTokens'][$token])) { if (!$preformatted) $word = $this->postProcess($word); if ($this->_setHighlightCache('highlightTutorialSourceTokens', $token)) { return $word; } $e = $this->_appendHighlight; return $e . $this->template_options['highlightTutorialSourceTokens'][$token] . $word; } else { $this->_setHighlightCache(false, false); $e = $this->_appendHighlight; return $e . ($preformatted ? $word : $this->postProcess($word)); } } /** * Called by {@link parserReturnTag::Convert()} to allow converters to * change type names to desired formatting * * Used by {@link XMLDocBookConverter::type_adjust()} to change true and * false to the peardoc2 values * @param string * @return string */ function type_adjust($typename) { return $typename; } /** * Used to convert the {@}example} inline tag in a docblock. * * By default, this just wraps ProgramExample * @see XMLDocBookpeardoc2Converter::exampleProgramExample * @param string * @param boolean true if this is to highlight a tutorial <programlisting> * @return string */ function exampleProgramExample($example, $tutorial = false, $inlinesourceparse = null/*false*/, $class = null/*false*/, $linenum = null/*false*/, $filesourcepath = null/*false*/) { return $this->ProgramExample($example, $tutorial, $inlinesourceparse, $class, $linenum, $filesourcepath); } /** * Used to convert the <<code>> tag in a docblock * @param string * @param boolean true if this is to highlight a tutorial <programlisting> * @return string */ function ProgramExample($example, $tutorial = false, $inlinesourceparse = null/*false*/, $class = null/*false*/, $linenum = null/*false*/, $filesourcepath = null/*false*/) { if (tokenizer_ext) { $e = $example; if (!is_array($example)) { $obj = new phpDocumentorTWordParser; $obj->setup($example); $e = $obj->getFileSource(); if (!isset($e[0]) || !is_array($e[0][0]) || $e[0][0][0] != T_OPEN_TAG) { $example = "<?php\n".$example; $obj->setup($example); $e = $obj->getFileSource(); unset($e[0]); $e = array_values($e); } unset($obj); } $saveclass = $this->class; $parser = new phpDocumentor_HighlightParser; if (!isset($inlinesourceparse)) { $example = $parser->parse($e, $this, true); // force php mode } else { if (isset($filesourcepath)) { $example = $parser->parse($e, $this, $inlinesourceparse, $class, $linenum, $filesourcepath); } elseif (isset($linenum)) { $example = $parser->parse($e, $this, $inlinesourceparse, $class, $linenum); } elseif (isset($class)) { $example = $parser->parse($e, $this, $inlinesourceparse, $class); } else { $example = $parser->parse($e, $this, $inlinesourceparse); } } $this->class = $saveclass; } else { $example = $this->postProcess($example); } if ($tutorial) { return $example; } if (!isset($this->template_options['desctranslate'])) return $example; if (!isset($this->template_options['desctranslate']['code'])) return $example; $example = $this->template_options['desctranslate']['code'] . $example; if (!isset($this->template_options['desctranslate']['/code'])) return $example; return $example . $this->template_options['desctranslate']['/code']; } /** * @param string */ function TutorialExample($example) { $parse = new phpDocumentor_TutorialHighlightParser; return $parse->parse($example, $this); } /** * Used to convert the contents of <<li>> in a docblock * @param string * @return string */ function ListItem($item) { if (!isset($this->template_options['desctranslate'])) return $item; if (!isset($this->template_options['desctranslate']['li'])) return $item; $item = $this->template_options['desctranslate']['li'] . $item; if (!isset($this->template_options['desctranslate']['/li'])) return $item; return $item . $this->template_options['desctranslate']['/li']; } /** * Used to convert the contents of <<ol>> or <<ul>> in a docblock * @param string * @return string */ function EncloseList($list,$ordered) { $listname = ($ordered ? 'ol' : 'ul'); if (!isset($this->template_options['desctranslate'])) return $list; if (!isset($this->template_options['desctranslate'][$listname])) return $list; $list = $this->template_options['desctranslate'][$listname] . $list; if (!isset($this->template_options['desctranslate']['/'.$listname])) return $list; return $list . $this->template_options['desctranslate']['/'.$listname]; } /** * Used to convert the contents of <<pre>> in a docblock * @param string * @return string */ function PreserveWhiteSpace($string) { if (!isset($this->template_options['desctranslate'])) return $string; if (!isset($this->template_options['desctranslate']['pre'])) return $string; $string = $this->template_options['desctranslate']['pre'] . $string; if (!isset($this->template_options['desctranslate']['/pre'])) return $string; return $string . $this->template_options['desctranslate']['/pre']; } /** * Used to enclose a paragraph in a docblock * @param string * @return string */ function EncloseParagraph($para) { if (!isset($this->template_options['desctranslate'])) return $para; if (!isset($this->template_options['desctranslate']['p'])) return $para; $para = $this->template_options['desctranslate']['p'] . $para; if (!isset($this->template_options['desctranslate']['/p'])) return $para; return $para . $this->template_options['desctranslate']['/p']; } /** * Used to convert the contents of <<b>> in a docblock * @param string * @return string */ function Bolden($para) { if (!isset($this->template_options['desctranslate'])) return $para; if (!isset($this->template_options['desctranslate']['b'])) return $para; $para = $this->template_options['desctranslate']['b'] . $para; if (!isset($this->template_options['desctranslate']['/b'])) return $para; return $para . $this->template_options['desctranslate']['/b']; } /** * Used to convert the contents of <<i>> in a docblock * @param string * @return string */ function Italicize($para) { if (!isset($this->template_options['desctranslate'])) return $para; if (!isset($this->template_options['desctranslate']['i'])) return $para; $para = $this->template_options['desctranslate']['i'] . $para; if (!isset($this->template_options['desctranslate']['/i'])) return $para; return $para . $this->template_options['desctranslate']['/i']; } /** * Used to convert the contents of <<var>> in a docblock * @param string * @return string */ function Varize($para) { if (!isset($this->template_options['desctranslate'])) return $para; if (!isset($this->template_options['desctranslate']['var'])) return $para; $para = $this->template_options['desctranslate']['var'] . $para; if (!isset($this->template_options['desctranslate']['/var'])) return $para; return $para . $this->template_options['desctranslate']['/var']; } /** * Used to convert the contents of <<kbd>> in a docblock * @param string * @return string */ function Kbdize($para) { if (!isset($this->template_options['desctranslate'])) return $para; if (!isset($this->template_options['desctranslate']['kbd'])) return $para; $para = $this->template_options['desctranslate']['kbd'] . $para; if (!isset($this->template_options['desctranslate']['/kbd'])) return $para; return $para . $this->template_options['desctranslate']['/kbd']; } /** * Used to convert the contents of <<samp>> in a docblock * @param string * @return string */ function Sampize($para) { if (!isset($this->template_options['desctranslate'])) return $para; if (!isset($this->template_options['desctranslate']['samp'])) return $para; $para = $this->template_options['desctranslate']['samp'] . $para; if (!isset($this->template_options['desctranslate']['/samp'])) return $para; return $para . $this->template_options['desctranslate']['/samp']; } /** * Used to convert <<br>> in a docblock * @param string * @return string */ function Br($para) { if (!isset($this->template_options['desctranslate'])) return $para; if (!isset($this->template_options['desctranslate']['br'])) return $para; $para = $this->template_options['desctranslate']['br'] . $para; return $para; } /** * This version does nothing * * Perform necessary post-processing of string data. For example, the HTML * Converters should escape < and > to become < and > * @return string */ function postProcess($text) { return $text; } /** * Creates a table of contents for a {@}toc} inline tag in a tutorial * * This function should return a formatted table of contents. By default, it * does nothing, it is up to the converter to format the TOC * @abstract * @return string table of contents formatted for use in the current output format * @param array format: array(array('tagname' => section, 'link' => returnsee link, 'id' => anchor name, 'title' => from title tag),...) */ function formatTutorialTOC($toc) { return ''; } /** * Write out the formatted source code for a php file * * This function provides the primary functionality for the * {@tutorial tags.filesource.pkg} tag. * @param string full path to the file * @param string fully highlighted/linked source code of the file * @abstract */ function writeSource($filepath, $source) { debug($source); return; } /** * Write out the formatted source code for an example php file * * This function provides the primary functionality for the * {@tutorial tags.example.pkg} tag. * @param string example title * @param string example filename (no path) * @param string fully highlighted/linked source code of the file * @abstract */ function writeExample($title, $path, $source) { return; } /** Translate the path info into a unique file name for the highlighted * source code. * @param string $pathinfo * @return string */ function getFileSourceName($path) { global $_phpDocumentor_options; $pathinfo = $this->proceduralpages->getPathInfo($path, $this); $pathinfo['source_loc'] = str_replace($_phpDocumentor_options['Program_Root'].'/','',$pathinfo['source_loc']); $pathinfo['source_loc'] = str_replace('/','_',$pathinfo['source_loc']); return "fsource_{$pathinfo['package']}_{$pathinfo['subpackage']}_{$pathinfo['source_loc']}"; } /** Return the fixed path to the source-code file folder. * @param string $base Path is relative to this folder * @return string */ function getFileSourcePath($base) { if (substr($base, -1) != PATH_DELIMITER) { $base .= PATH_DELIMITER; } return $base . '__filesource'; } /** Return the path to the current * @param string $pathinfo * @return string */ function getCurrentPageURL() { return '{$srcdir}' . PATH_DELIMITER . $this->page_dir; } /** * @return string an output-format dependent link to phpxref-style highlighted * source code * @abstract */ function getSourceLink($path) { return ''; } /** * @return string Link to the current page being parsed. * Should return {@link $curname} and a converter-specific extension. * @abstract */ function getCurrentPageLink() { } /** * Return a line of highlighted source code with formatted line number * * If the $path is a full path, then an anchor to the line number will be * added as well * @param integer line number * @param string highlighted source code line * @param false|string full path to @filesource file this line is a part of, * if this is a single line from a complete file. * @return string formatted source code line with line number */ function sourceLine($linenumber, $line, $path = false) { if ($path) { return $this->getSourceAnchor($path, $linenumber) . $this->Br(sprintf('%-6u',$linenumber).str_replace("\n",'',$line)); } else { return $this->Br(sprintf('%-6u',$linenumber).str_replace("\n",'',$line)); } } /** * Determine whether an element's file has generated source code, used for * linking to line numbers of source. * * Wrapper for {@link $sourcePaths} in this version * @param string full path to the source code file * @return boolean */ function hasSourceCode($path) { return isset($this->sourcePaths[$path]); } /** * Mark a file as having had source code highlighted * @param string full path of source file */ function setSourcePaths($path) { $this->sourcePaths[$path] = true; } /** * Used to translate an XML DocBook entity like ” from a tutorial by * reading the options.ini file for the template. * @param string entity name */ function TranslateEntity($name) { if (!isset($this->template_options['ppage'])) { if (!$this->template_options['preservedocbooktags']) return ''; else return '&'.$name.';'; } if (isset($this->template_options['ppage']['&'.$name.';'])) { return $this->template_options['ppage']['&'.$name.';']; } else { if (!$this->template_options['preservedocbooktags']) return ''; else return '&'.$name.';'; } } /** * Used to translate an XML DocBook tag from a tutorial by reading the * options.ini file for the template. * @param string tag name * @param string any attributes Format: array(name => value) * @param string the tag contents, if any * @param string the tag contents, if any, unpost-processed * @return string */ function TranslateTag($name,$attr,$cdata,$unconvertedcdata) { if (!isset($this->template_options['ppage'])) { if (!$this->template_options['preservedocbooktags']) return $cdata; else return '<'.$name.$this->AttrToString($name,$attr,true).'>'.$cdata.'</'.$name.'>'."\n"; } // make sure this template transforms the tag into something if (isset($this->template_options['ppage'][$name])) { // test for global attribute transforms like $attr$role = class, changing // all role="*" attributes to class="*" in html, for example foreach($attr as $att => $val) { if (isset($this->template_options['$attr$'.$att])) { $new = ''; if (!isset($this->template_options['$attr$'.$att]['close'])) { $new .= '<'.$this->template_options['$attr$'.$att]['open']; if (isset($this->template_options['$attr$'.$att]['cdata!'])) { if (isset($this->template_options['$attr$'.$att]['separateall'])) $new .= $this->template_options['$attr$'.$att]['separator']; else $new .= ' '; $new .= $this->template_options['$attr$'.$att]['$'.$att]; $new .= $this->template_options['$attr$'.$att]['separator']; if ($this->template_options['$attr$'.$att]['quotevalues']) $val = '"'.$val.'"'; $new .= $val.'>'; } else { $new .= '>'.$val; } $new .= '</'.$this->template_options['$attr$'.$att]['open'].'>'; } else { $new .= $this->template_options['$attr$'.$att]['open'] . $val . $this->template_options['$attr$'.$att]['close']; } unset($attr[$att]); $cdata = $new . $cdata; } } if (!isset($this->template_options['ppage']['/'.$name])) {// if the close tag isn't specified, we put opening and closing tags around it, with translated attributes if (isset($this->template_options['ppage'][$name.'/'])) $cdata = '<'.$this->template_options['ppage'][$name].$this->AttrToString($name,$attr).'/>' . $cdata; else $cdata = '<'.$this->template_options['ppage'][$name].$this->AttrToString($name,$attr).'>' . $cdata . '</'.$this->template_options['ppage'][$name].'>'; } else { // if close tag is specified, use the open and close as literal if ($name == 'programlisting' && isset($attr['role']) && ($attr['role'] == 'php' || $attr['role'] == 'tutorial' || $attr['role'] == 'html')) { // highlight PHP source // var_dump($unconvertedcdata, $cdata);exit; if ($attr['role'] == 'php') { $cdata = $this->ProgramExample($unconvertedcdata, true); } elseif ($attr['role'] == 'tutorial') { $cdata = $this->TutorialExample($unconvertedcdata); } elseif ($attr['role'] == 'html') { $cdata = $unconvertedcdata; } } else {// normal case below $cdata = $this->template_options['ppage'][$name].$this->AttrToString($name,$attr). $cdata .$this->template_options['ppage']['/'.$name]; } } return $cdata; } else { if ($this->template_options['preservedocbooktags']) { return '<'.$name.$this->AttrToString($name,$attr,true).'>' . $cdata . '</'.$name.'>'."\n"; } else { return $cdata; } } } /** * Convert the attribute of a Tutorial docbook tag's attribute list * to a string based on the template options.ini * @param string tag name * @param attribute array * @param boolean if true, returns attrname="value"... * @return string */ function AttrToString($tag,$attr,$unmodified = false) { $ret = ''; if ($unmodified) { $ret = ' '; foreach($attr as $n => $v) { $ret .= $n.' = "'.$v.'"'; } return $ret; } // no_attr tells us to ignore all attributes if (isset($this->template_options['no_attr'])) return $ret; // tagname! tells us to ignore all attributes for this tag if (isset($this->template_options['ppage'][$tag.'!'])) return $ret; if (count($attr)) $ret = ' '; // pass 1, check to see if any attributes add together $same = array(); foreach($attr as $n => $v) { if (isset($this->template_options['ppage'][$tag.'->'.$n])) { $same[$this->template_options['ppage'][$tag.'->'.$n]][] = $n; } } foreach($attr as $n => $v) { if (isset($this->template_options['ppage'][$tag.'->'.$n])) { if (count($same[$this->template_options['ppage'][$tag.'->'.$n]]) == 1) { // only 1 attribute translated for this one // this is useful for equivalent value names if (isset($this->template_options['ppage'][$tag.'->'.$n.'+'.$v])) $v = $this->template_options['ppage'][$tag.'->'.$n.'+'.$v]; } else { // more than 1 attribute combines to make the new attribute $teststrtemp = array(); foreach($same[$this->template_options['ppage'][$tag.'->'.$n]] as $oldattr) { $teststrtemp[] = $oldattr.'+'.$attr[$oldattr]; } $teststrs = array(); $num = count($same[$this->template_options['ppage'][$tag.'->'.$n]]); for($i=0;$i<$num;$i++) { $started = false; $a = ''; for($j=$i;!$started || $j != $i;$j = ($j + $i) % $num) { if (!empty($a)) $a .= '|'; $a .= $teststrtemp[$j]; } $teststrs[$i] = $a; } $done = false; foreach($teststrs as $test) { if ($done) break; if (isset($this->template_options['ppage'][$tag.'->'.$test])) { $done = true; $v = $this->template_options['ppage'][$tag.'->'.$test]; } } } $ret .= $this->template_options['ppage'][$tag.'->'.$n].' = "'.$v.'"'; } else { if (!isset($this->template_options['ppage'][$tag.'!'.$n])) { if (isset($this->template_options['ppage']['$attr$'.$n])) $ret .= $this->template_options['ppage']['$attr$'.$n].' = "'.$v.'"'; else $ret .= $n.' = "'.$v.'"'; } } } return $ret; } /** * Convert the title of a Tutorial docbook tag section * to a string based on the template options.ini * @param string tag name * @param array * @param string title text * @param string * @return string */ function ConvertTitle($tag,$attr,$title,$cdata) { if (!isset($this->template_options[$tag.'_title'])) return array($attr,$title.$cdata); if (isset($this->template_options[$tag.'_title']['tag_attr'])) { $attr[$this->template_options[$tag.'_title']['tag_attr']] = urlencode($cdata); $cdata = ''; } elseif(isset($this->template_options[$tag.'_title']['cdata_start'])) { $cdata = $this->template_options[$tag.'_title']['open'] . $title . $this->template_options[$tag.'_title']['close'] . $cdata; } else $cdata = $title.$cdata; return array($attr,$cdata); } /** * Return a converter-specific id to distinguish tutorials and their * sections * * Used by {@}id} * @return string */ function getTutorialId($package,$subpackage,$tutorial,$id) { return $package.$subpackage.$tutorial.$id; } /** * Create the {@link $elements, $pkg_elements} and {@link $links} arrays * @access private * @todo version 2.0 - faulty package_output logic should be removed * * in this version, if the parent file isn't in the package, all * the procedural elements are simply shunted to another package! */ function _createPkgElements(&$pages) { if (empty($this->elements)) { $this->elements = array(); $this->pkg_elements = array(); $this->links = array(); phpDocumentor_out('Building indexes...'); flush(); foreach($pages as $j => $flub) { $this->package = $pages[$j]->parent->package; $this->subpackage = $pages[$j]->parent->subpackage; $this->class = false; $this->curfile = $pages[$j]->parent->getFile(); $this->curname = $this->getPageName($pages[$j]->parent); $this->curpath = $pages[$j]->parent->getPath(); $use = true; if ($this->package_output) { if (in_array($this->package,$this->package_output)) { $this->addElement($pages[$j]->parent,$pages[$j]); } else { if (count($pages[$j]->classelements)) { list(,$pages[$j]->parent->package) = each($this->package_output); reset($this->package_output); $pages[$j]->parent->subpackage = ''; $this->addElement($pages[$j]->parent,$pages[$j]); } else { unset($pages[$j]); continue; } } } else { $this->addElement($pages[$j]->parent,$pages[$j]); } if ($use) for($i=0; $i<count($pages[$j]->elements); $i++) { $pages[$j]->elements[$i]->docblock->package = $this->package; $pages[$j]->elements[$i]->docblock->subpackage = $this->subpackage; $this->proceduralpages->replaceElement($pages[$j]->elements[$i]); $this->addElement($pages[$j]->elements[$i]); } for($i=0; $i<count($pages[$j]->classelements); $i++) { if ($this->class) { if ($pages[$j]->classelements[$i]->type == 'class') { if ($this->checkKillClass($pages[$j]->classelements[$i]->getName(),$pages[$j]->classelements[$i]->getPath())) continue; $this->package = $pages[$j]->classelements[$i]->docblock->package; if ($this->package_output) if (!in_array($this->package,$this->package_output)) continue; $this->subpackage = $pages[$j]->classelements[$i]->docblock->subpackage; $this->class = $pages[$j]->classelements[$i]->name; } else { if ($this->killclass) continue; // force all contained elements to have parent package/subpackage $pages[$j]->classelements[$i]->docblock->package = $this->package; $pages[$j]->classelements[$i]->docblock->subpackage = $this->subpackage; } } if ($pages[$j]->classelements[$i]->type == 'class') { if ($this->checkKillClass($pages[$j]->classelements[$i]->getName(),$pages[$j]->classelements[$i]->getPath())) continue; $this->package = $pages[$j]->classelements[$i]->docblock->package; if ($this->package_output) if (!in_array($this->package,$this->package_output)) continue; $this->subpackage = $pages[$j]->classelements[$i]->docblock->subpackage; $this->class = $pages[$j]->classelements[$i]->name; } if (!$this->killclass) $this->addElement($pages[$j]->classelements[$i]); } } phpDocumentor_out("done\n"); flush(); } $this->sortIndexes(); $this->sortTodos(); if ($this->sort_page_contents_by_type) $this->sortPageContentsByElementType($pages); } /** * Process the {@link $tutorials} array * * Using the tutorialname.ext.ini files, this method sets up tutorial * hierarchy. There is some minimal error checking to make sure that no * tutorial links to itself, even two levels deep as in tute->next->tute. * * If all tests pass, it creates the hierarchy * @uses generateTutorialOrder() * @uses _setupTutorialTree() * @access private */ function _processTutorials() { $parents = $all = array(); foreach($this->tutorials as $package => $els) { if ($this->package_output) { if (!in_array($package,$this->package_output)) { unset($this->tutorials[$package]); continue; } } if (!isset($this->pkg_elements[$package])) { unset($this->tutorials[$package]); continue; } foreach($els as $subpackage => $els2) { foreach($els2 as $type => $tutorials) { foreach($tutorials as $tutorial) { if ($tutorial->ini) { if (isset($tutorial->ini['Linked Tutorials'])) { foreach($tutorial->ini['Linked Tutorials'] as $child) { $sub = (empty($tutorial->subpackage) ? '' : $tutorial->subpackage . '/'); $kid = $tutorial->package . '/' . $sub . $child . '.' . $tutorial->tutorial_type; // parent includes self as a linked tutorial? $kidlink = $this->getTutorialLink($kid,false,false,array($tutorial->package)); if (is_object($kidlink) && $this->returnSee($kidlink) == $tutorial->getLink($this)) { // bad! addErrorDie(PDERROR_TUTORIAL_IS_OWN_CHILD,$tutorial->name,$tutorial->name.'.ini'); } } $parents[] = $tutorial; } } $all[$package][$subpackage][$type][] = $tutorial; } } } } // loop error-checking, use this to eliminate possibility of accidentally linking to a parent as a child $testlinks = array(); foreach($parents as $parent) { $testlinks[$parent->name]['links'][] = $parent->getLink($this); $testlinks[$parent->name]['name'][$parent->getLink($this)] = $parent->name; } // generate the order of tutorials, and link them together foreach($parents as $parent) { foreach($parent->ini['Linked Tutorials'] as $child) { $sub = (empty($parent->subpackage) ? '' : $parent->subpackage . '/'); $kid = $parent->package . '/' . $sub . $child . '.' . $parent->tutorial_type; // child tutorials must be in the same package AND subpackage // AND have the same extension as the parent, makes things clearer for both ends if (in_array($this->returnSee($this->getTutorialLink($kid,false,false,array($parent->package))),$testlinks[$parent->name]['links'])) addErrorDie(PDERROR_TUTORIAL_IS_OWN_GRANDPA,$testlinks[$parent->name][$this->returnSee($this->getTutorialLink($kid,false,false,array($parent->package)))],$kid->name,$testlinks[$parent->name][$this->returnSee($this->getTutorialLink($kid,false,false,array($parent->package)))],$kid->name.'.ini'); if ($this->returnSee($this->getTutorialLink($kid,false,false,array($parent->package))) == $kid) { addWarning(PDERROR_CHILD_TUTORIAL_NOT_FOUND, $child . '.' . $parent->tutorial_type, $parent->name .'.ini',$parent->package, $parent->subpackage); } } } $new = $tree = $roots = array(); // build a list of all 'root' tutorials (tutorials without parents). foreach($parents as $i => $parent) { if (! $parent->isChildOf($parents)) { $roots[] = $parent; } } $parents = $roots; // add the parents and all child tutorials in order to the list of tutorials to process foreach($parents as $parent) { $this->generateTutorialOrder($parent,$all,$new); } if (count($all)) { // add the leftover tutorials foreach($all as $package => $els) { foreach($els as $subpackage => $els2) { foreach($els2 as $type => $tutorials) { foreach($tutorials as $tutorial) { $new[$package][$subpackage][$type][] = $tutorial; } } } } } // remove the old, unprocessed tutorials, and set it up with the next code $this->tutorials = array(); // reset integrity of the tutorial list $prev = false; uksort($new, 'tutorialcmp'); // debug($this->vardump_tree($new));exit; foreach($new as $package => $els) { foreach($els as $subpackage => $els2) { foreach($els2 as $type => $tutorials) { foreach($tutorials as $tutorial) { if ($prev) { $this->tutorials[$prevpackage][$prevsubpackage][$prevtype][$prevname]->setNext($tutorial,$this); $tutorial->setPrev($prev,$this); } $this->tutorials[$package][$subpackage][$type][$tutorial->name] = $tutorial; $prev = $tutorial->getLink($this,true); $prevpackage = $package; $prevsubpackage = $subpackage; $prevtype = $type; $prevname = $tutorial->name; } } } } $this->tutorial_tree = $this->_setupTutorialTree(); return $new; } /** * called by {@link phpDocumentor_IntermediateParser::Convert()} to traverse * the array of pages and their elements, converting them to the output format * * The walk() method should be flexible enough such that it never needs * modification. walk() sets up all of the indexes, and sorts everything in * logical alphabetical order. It then passes each element individually to * {@link Convert()}, which then passes to the Convert*() methods. A child * Converter need not override any of these unless special functionality must * be added. see {@tutorial Converters/template.vars.cls} for details. * {@internal * walk() first creates all of the indexes {@link $elements, $pkg_elements} * and the left indexes specified by {@link $leftindexes}, * and then sorts them by calling {@link sortIndexes()}. * * Next, it converts all README/CHANGELOG/INSTALL-style files, using * {@link Convert_RIC}. * * After this, it * passes all package-level docs to Convert(). Then, it calls the index * sorting functions {@link formatPkgIndex(), formatIndex()} and * {@link formatLeftIndex()}. * * Finally, it converts each procedural page in alphabetical order. This * stage passes elements from the physical file to Convert() in alphabetical * order. First, procedural page elements {@link parserDefine, parserInclude} * {@link parserGlobal}, and {@link parserFunction} are passed to Convert(). * * Then, class elements are passed in this order: {@link parserClass}, then * all of the {@link parserVar}s in the class and all of the * {@link parserMethod}s in the class. Classes are in alphabetical order, * and both vars and methods are in alphabetical order. * * Finally, {@link ConvertErrorLog()} is called and the data walk is complete.}} * @param array Format: array(fullpath => {@link parserData} structure with full {@link parserData::$elements} * and {@link parserData::$class_elements}. * @param array Format: array({@link parserPackagePage} 1, {@link parserPackagePage} 2,...) * @uses Converter::_createPkgElements() sets up {@link $elements} and * {@link $pkg_elements} array, as well as {@link $links} */ function walk(&$pages,&$package_pages) { if (empty($pages)) { die("<b>ERROR</b>: nothing parsed"); } $this->_createPkgElements($pages); if (count($this->ric)) { phpDocumentor_out("Converting README/INSTALL/CHANGELOG contents...\n"); flush(); foreach($this->ric as $name => $contents) { phpDocumentor_out("$name..."); flush(); $this->Convert_RIC($name,$contents); } phpDocumentor_out("\ndone\n"); flush(); } foreach($package_pages as $i => $perp) { if ($this->package_output) { if (!in_array($package_pages[$i]->package,$this->package_output)) continue; } phpDocumentor_out('Converting package page for package '.$package_pages[$i]->package.'... '); flush(); $this->package = $package_pages[$i]->package; $this->subpackage = ''; $this->class = false; $this->Convert($package_pages[$i]); phpDocumentor_out("done\n"); flush(); } phpDocumentor_out("Converting tutorials/extended docs\n"); flush(); // get tutorials into the order they will display, and set next/prev links $new = $this->_processTutorials(); foreach($this->tutorials as $package => $els) { foreach($els as $subpackage => $els2) { foreach($els2 as $type => $tutorials) { foreach($tutorials as $tutorial) { switch ($type) { case 'pkg' : $a = ''; if ($tutorial->ini) $a .= 'Top-level '; if (!empty($tutorial->subpackage)) $a .= 'Sub-'; $ptext = "Converting ${a}Package-level tutorial ".$tutorial->name.'...'; break; case 'cls' : $a = ''; if ($tutorial->ini) $a .= 'Top-level '; $ptext = "Converting ${a}Class-level tutorial " . $tutorial->name ." and associating..."; $link = Converter::getClassLink(str_replace('.cls','',$tutorial->name), $tutorial->package); if (is_object($link)) { if ($this->sort_absolutely_everything) { $addend = 'unsuccessful '; if (isset($this->package_elements[$tutorial->package][$tutorial->subpackage]['class'][$link->name])) { $this->package_elements[$tutorial->package][$tutorial->subpackage]['class'][$link->name][0]->addTutorial($tutorial,$this); $addend = 'success '; } } else { $addend = 'unsuccessful '; if (!isset($this->classes->killclass[str_replace('.cls','',$tutorial->name)]) && !isset($this->classes->killclass[str_replace('.cls','',$tutorial->name)][$tutorial->path])) { foreach($pages as $j => $inf) { foreach($inf->classelements as $i => $class) { if ($class->type == 'class' && $class->name == str_replace('.cls','',$tutorial->name) && $class->path == $link->path) { $pages[$j]->classelements[$i]->addTutorial($tutorial,$this); $addend = 'success '; } } } } } $ptext .= $addend; } else $ptext .= "unsuccessful "; break; case 'proc' : $a = ''; if ($tutorial->ini) $a .= 'Top-level '; $ptext = "Converting ${a}Procedural-level tutorial ".$tutorial->name." and associating..."; $link = Converter::getPageLink(str_replace('.proc','',$tutorial->name), $tutorial->package); if (is_object($link)) { $addend = 'unsuccessful '; if ($this->sort_absolutely_everything) { if (isset($this->package_elements[$tutorial->package][$tutorial->subpackage]['page'][$link->path])) { $this->package_elements[$tutorial->package][$tutorial->subpackage]['page'][$link->path][0]->addTutorial($tutorial,$this); $addend = "success "; } } else { foreach($pages as $j => $info) { if ($j == $link->path) { $pages[$j]->addTutorial($tutorial,$this); $addend = "success "; } } } $ptext .= $addend; } else $ptext .= "unsuccessful "; break; } phpDocumentor_out($ptext); flush(); $this->package = $tutorial->package; $this->subpackage = $tutorial->subpackage; $this->Convert($tutorial); phpDocumentor_out("done\n"); flush(); } } } } phpDocumentor_out("Formatting Package Indexes..."); flush(); $this->formatPkgIndex(); phpDocumentor_out("done\n"); flush(); phpDocumentor_out("Formatting Index..."); flush(); $this->formatIndex(); phpDocumentor_out("done\n\n"); flush(); phpDocumentor_out("Formatting Left Quick Index..."); flush(); $this->formatLeftIndex(); phpDocumentor_out("done\n\n"); flush(); if ($this->sort_absolutely_everything) return $this->walk_everything(); foreach($pages as $j => $flub) { phpDocumentor_out('Converting '.$pages[$j]->parent->getPath()); flush(); $this->package = $pages[$j]->parent->package; $this->subpackage = $pages[$j]->parent->subpackage; $this->class = false; $this->curfile = $pages[$j]->parent->getFile(); $this->curname = $this->getPageName($pages[$j]->parent); $this->curpath = $pages[$j]->parent->getPath(); $use = true; if ($this->package_output) { if (in_array($this->package,$this->package_output)) { $this->Convert($pages[$j]); } else { $use = false; } } else { $this->Convert($pages[$j]); } phpDocumentor_out(" Procedural Page Elements..."); flush(); if ($use) for($i=0; $i<count($pages[$j]->elements); $i++) { $a = $pages[$j]->elements[$i]->docblock->getKeyword('access'); if (is_object($a)) $a = $a->getString(); if (!$this->parseprivate && ($a == 'private')) continue; // phpDocumentor_out(" ".$pages[$j]->elements[$i]->name."\n"); $pages[$j]->elements[$i]->docblock->package = $this->package; $pages[$j]->elements[$i]->docblock->subpackage = $this->subpackage; $this->Convert($pages[$j]->elements[$i]); } phpDocumentor_out(" Classes..."); $this->class = false; flush(); for($i=0; $i<count($pages[$j]->classelements); $i++) { if ($this->class) { if ($pages[$j]->classelements[$i]->type == 'class') { if (!$this->killclass) $this->endClass(); $this->killclass = false; if ($this->checkKillClass($pages[$j]->classelements[$i]->getName(),$pages[$j]->classelements[$i]->getPath())) continue; $this->package = $pages[$j]->classelements[$i]->docblock->package; if ($this->package_output) if (!in_array($this->package,$this->package_output)) continue; $this->subpackage = $pages[$j]->classelements[$i]->docblock->subpackage; $this->class = $pages[$j]->classelements[$i]->name; } else { $a = $pages[$j]->classelements[$i]->docblock->getKeyword('access'); if (is_object($a)) $a = $a->getString(); if (!$this->parseprivate && ($a == 'private')) continue; if ($this->killclass) continue; // force all contained elements to have parent package/subpackage $pages[$j]->classelements[$i]->docblock->package = $this->package; $pages[$j]->classelements[$i]->docblock->subpackage = $this->subpackage; } } if ($pages[$j]->classelements[$i]->type == 'class') { $this->killclass = false; if ($this->checkKillClass($pages[$j]->classelements[$i]->getName(),$pages[$j]->classelements[$i]->getPath())) continue; $this->package = $pages[$j]->classelements[$i]->docblock->package; if ($this->package_output) if (!in_array($this->package,$this->package_output)) continue; $this->subpackage = $pages[$j]->classelements[$i]->docblock->subpackage; $this->class = $pages[$j]->classelements[$i]->name; } if ($this->killclass) continue; // phpDocumentor_out(" ".$pages[$j]->classelements[$i]->name."\n"); $this->Convert($pages[$j]->classelements[$i]); } if (count($pages[$j]->classelements) && !$this->killclass) $this->endClass(); phpDocumentor_out(" done\n"); flush(); $this->endPage(); } phpDocumentor_out("\nConverting @todo List..."); flush(); if (count($this->todoList)) { $this->ConvertTodoList(); } phpDocumentor_out("done\n"); flush(); phpDocumentor_out("\nConverting Error Log..."); flush(); $this->ConvertErrorLog(); phpDocumentor_out("done\n"); flush(); } /** * Get a tree structure representing the hierarchy of tutorials * * Returns an array in format: * <pre> * array('tutorial' => {@link parserTutorial}, * 'kids' => array( // child tutorials * array('tutorial' => child {@link parserTutorial}, * 'kids' => array(...) * ) * ) * ) * </pre> * @param parserTutorial|array * @tutorial tutorials.pkg * @return array */ function getTutorialTree($tutorial) { if (is_object($tutorial)) { $path = $tutorial->package . '/' . $tutorial->subpackage . '/' . $tutorial->name; if (isset($this->tutorial_tree[$path])) $tutorial = $this->tutorial_tree[$path]; else return false; } $tree = array(); if (isset($tutorial['tutorial'])) { $tree['tutorial'] = $tutorial['tutorial']; if (isset($tutorial['child'])) { foreach($tutorial['child'] as $a => $b) { $btut = $b['tutorial']; $res['tutorial'] = $this->tutorials[$btut->package][$btut->subpackage][$btut->tutorial_type][$btut->name]; if (isset($b['child'])) { $tempres = Converter::getTutorialTree($b); $res['kids'] = $tempres['kids']; } $tree['kids'][] = $res; } } } return $tree; } /** * Remove tutorials one by one from $all, and transfer them into $new in the * order they should be parsed * @param parserTutorial * @param array * @param array * @access private */ function generateTutorialOrder($parent,&$all,&$new) { // remove from the list of tutorials to process foreach($all[$parent->package][$parent->subpackage][$parent->tutorial_type] as $ind => $t) { if ($t->name == $parent->name) { unset($all[$parent->package][$parent->subpackage][$parent->tutorial_type][$ind]); } } // add to the new ordered list of tutorials $x = &$new[$parent->package][$parent->subpackage][$parent->tutorial_type]; if (!is_object($x[count($x) - 1]) || $x[count($x) - 1]->name != $parent->name) { // only add if the parent isn't also a child $new[$parent->package][$parent->subpackage][$parent->tutorial_type][] = $parent; // add a new branch to the tree } // process all child tutorials, and insert them in order // debug("processing parent ".$parent->name); if ($parent->ini) { foreach($parent->ini['Linked Tutorials'] as $child) { $sub = (empty($parent->subpackage) ? '' : $parent->subpackage . '/'); $kid = $parent->package . '/' . $sub . $child . '.' . $parent->tutorial_type; $_klink = $this->getTutorialLink($kid,false,false,array($parent->package)); if (is_object($_klink)) { $klink = $this->returnSee($_klink); } else { $klink = false; } // remove the child from the list of remaining tutorials foreach($all[$parent->package][$parent->subpackage][$parent->tutorial_type] as $ind => $tute) { if ($klink && $tute->getLink($this) == $klink) { // set up parent, next and prev links $tute->setParent($parent, $this); // remove the child from the list of tutorials to process foreach($all[$parent->package][$parent->subpackage][$parent->tutorial_type] as $ind => $t) { if ($t->name == $tute->name) unset($all[$parent->package][$parent->subpackage][$parent->tutorial_type][$ind]); } // add to the new ordered list of tutorials $new[$parent->package][$parent->subpackage][$parent->tutorial_type][] = $tute; if ($tute->ini) { // add all the child's child tutorials to the list $this->generateTutorialOrder($tute,$all,$new); } } } } } return; } /** Returns the path to this tutorial as a string * @param parserTutorial $pkg * @param parserTutorial $subpkg * @param parserTutorial $namepkg * @return string */ function _tutorial_path($pkg, $subpkg = 0, $namepkg = 0) { if (! $subpkg) $subpkg = $pkg; if (! $namepkg) $namepkg = $pkg; return $pkg->package.'/'.$subpkg->subpackage.'/'.$namepkg->name; } /** * Creates a tree structure of tutorials * * Format: * <pre> * array('package/subpackage/tutorial1.ext' => * array('tutorial' => {@link parserTutorial}, * 'child' => * array('package/subpackage/child1tutorial.ext' => ..., * 'package/subpackage/child2tutorial.ext' => ..., * ... * ) * 'package/subpackage/tutorial2.ext' => ..., * ... * ) * </pre> * @return array the tutorial tree * @access private */ function _setupTutorialTree($parent = false) { if (! isset($this->processed_tutorials)) $this->processed_tutorials = array(); $tree = array(); if (!$parent) { foreach($this->tutorials as $package => $s) { foreach($s as $subpackage => $t) { foreach($t as $type => $n) { foreach($n as $name => $tutorial) { if (!$tutorial->parent) { $child_path = $this->_tutorial_path($tutorial,$tutorial,$tutorial); if (! isset($this->processed_tutorials[$child_path])) { $this->processed_tutorials[$child_path] = $tutorial; //debug("parent ".$tutorial->name); $ret = $this->_setupTutorialTree($tutorial); if (!count($tree)) $tree = $ret; else $tree = array_merge($tree,$ret); } } } } } } return $tree; } $parent_path = $this->_tutorial_path($parent); $tree[$parent_path]['tutorial'] = $parent; // process all child tutorials, and insert them in order if ($parent->ini) { foreach($parent->ini['Linked Tutorials'] as $child) { if (isset($this->tutorials[$parent->package][$parent->subpackage][$parent->tutorial_type][$child . '.' . $parent->tutorial_type])) // remove the child from the list of remaining tutorials $tute = $this->tutorials[$parent->package][$parent->subpackage][$parent->tutorial_type][$child . '.' . $parent->tutorial_type]; else $tute = false; if ($tute) { $child_path = $this->_tutorial_path($parent,$parent,$tute); if (! isset($this->processed_tutorials[$child_path])) { $this->processed_tutorials[$child_path] = $tute; if ($tute->name == $child . '.' . $parent->tutorial_type) { // echo "Adding [$child_path] to [$parent_path]<br>"; $tree[$parent_path]['child'][$this->_tutorial_path($parent,$parent,$tute)]['tutorial'] = $tute; if ($tute->ini) { // add all the child's child tutorials to the list if (!isset($tree[$parent_path]['child'])) $tree[$parent_path]['child'] = $this->_setupTutorialTree($tute); else $tree[$parent_path]['child'] = array_merge($tree[$parent_path]['child'],$this->_setupTutorialTree($tute)); } } } } } } return $tree; } /** * Debugging function for dumping {@link $tutorial_tree} * @return string */ function vardump_tree($tree,$indent='') { if (phpDocumentor_get_class($tree) == 'parsertutorial') return $tree->name.' extends '.($tree->parent? $tree->parent->name : 'nothing'); $a = ''; foreach($tree as $ind => $stuff) { $x = $this->vardump_tree($stuff,"$indent "); $a .= $indent.'['.$ind." => \n ".$indent.$x."]\n"; } return substr($a,0,strlen($a) - 1); } /** * @access private */ function sort_package_elements($a,$b) { if (($a->type == $b->type) && (isset($a->isConstructor) && $a->isConstructor)) return -1; if (($a->type == $b->type) && (isset($b->isConstructor) && $b->isConstructor)) return 1; if ($a->type == $b->type) return strnatcasecmp($a->name,$b->name); if ($a->type == 'class') return -1; if ($b->type == 'class') return 1; if ($a->type == 'const') return -1; if ($b->type == 'const') return 1; if ($a->type == 'var') return -1; if ($b->type == 'var') return 1; if ($a->type == 'page') return -1; if ($b->type == 'page') return 1; if ($a->type == 'include') return -1; if ($b->type == 'include') return 1; if ($a->type == 'define') return -1; if ($b->type == 'define') return 1; if ($a->type == 'global') return -1; if ($b->type == 'global') return 1; if ($a->type == 'function') return -1; if ($b->type == 'function') return 1; } /** * @access private */ function defpackagesort($a,$b) { if ($a == $GLOBALS['phpDocumentor_DefaultPackageName']) return -1; if ($b == $GLOBALS['phpDocumentor_DefaultPackageName']) return 0; return strnatcasecmp($a,$b); } /** * @access private */ function Pc_sort($a,$b) { return strnatcasecmp(key($a),key($b)); } /** * walk over elements by package rather than page * * This method is designed for converters like the PDF converter that need * everything passed in alphabetical order by package/subpackage and by * procedural and then class information * @see PDFdefaultConverter * @see walk() */ function walk_everything() { global $hooser; $hooser = false; uksort($this->package_elements,array($this,'defpackagesort')); foreach($this->package_elements as $package => $r) { if ($this->package_output) { if (!in_array($this->package,$this->package_output)) { unset($this->package_elements[$package]); continue; } } uksort($this->package_elements[$package],'strnatcasecmp'); } foreach($this->package_elements as $package => $r) { foreach($this->package_elements[$package] as $subpackage => $r) { if (isset($r['page'])) { uksort($r['page'],'strnatcasecmp'); foreach($r['page'] as $page => $oo) { usort($this->package_elements[$package][$subpackage]['page'][$page],array($this,'sort_package_elements')); } } if (isset($r['class'])) { uksort($r['class'],'strnatcasecmp'); foreach($r['class'] as $page => $oo) { usort($r['class'][$page],array($this,'sort_package_elements')); } } $this->package_elements[$package][$subpackage] = $r; } } foreach($this->package_elements as $package => $s) { $notyet = false; foreach($s as $subpackage => $r) { $this->package = $package; $this->subpackage = $subpackage; if (isset($r['page'])) { $this->class = false; foreach($r['page'] as $page => $elements) { if (is_array($elements)) { foreach($elements as $element) { if ($element->type == 'page') { phpDocumentor_out('Converting '.$element->parent->getPath()); flush(); $this->curfile = $element->parent->getFile(); $this->curname = $this->getPageName($element->parent); $this->curpath = $element->parent->getPath(); $notyet = true; } else { // force all contained elements to have parent package/subpackage $element->docblock->package = $this->package; $element->docblock->subpackage = $this->subpackage; $a = $element->docblock->getKeyword('access'); if (is_object($a)) $a = $a->getString(); if (!$this->parseprivate && ($a == 'private')) continue; } if ($notyet) { phpDocumentor_out(" Procedural Page Elements..."); flush(); $notyet = false; } $this->Convert($element); } } $this->endPage(); phpDocumentor_out("done\n"); flush(); } } $start_classes = true; if (isset($r['class'])) { foreach($r['class'] as $class => $elements) { foreach($elements as $element) { if ($element->type == 'class') { if (!$start_classes) { if (count($elements) && !$this->killclass) $this->endClass(); phpDocumentor_out("done\n"); flush(); } $start_classes = false; $this->class = $element->getName(); $this->killclass = false; if ($this->checkKillClass($element->getName(),$element->getPath())) continue; if (!$this->killclass) { phpDocumentor_out('Converting '.$this->class."..."); flush(); $notyet = true; } } else { if ($notyet) { phpDocumentor_out("Variables/methods/Class constants...\n"); flush(); $notyet = false; } $a = $element->docblock->getKeyword('access'); if (is_object($a)) $a = $a->getString(); if (!$this->parseprivate && ($a == 'private')) continue; if ($this->killclass) continue; // force all contained elements to have parent package/subpackage $element->docblock->package = $this->package; $element->docblock->subpackage = $this->subpackage; } if ($this->killclass) continue; $this->Convert($element); } } if (count($elements) && !$this->killclass) $this->endClass(); phpDocumentor_out("done\n"); flush(); } // if isset($r['class']) } // foreach($s } // foreach($this->package_elements) phpDocumentor_out("\nConverting @todo List..."); flush(); if (count($this->todoList)) { $this->ConvertTodoList(); } phpDocumentor_out("done\n"); flush(); phpDocumentor_out("\nConverting Error Log..."); flush(); $this->ConvertErrorLog(); phpDocumentor_out("done\n"); flush(); } /** * Convert the phpDocumentor parsing/conversion error log * @abstract */ function ConvertErrorLog() { } /** * Convert the list of all @todo tags * @abstract */ function ConvertTodoList() { } /** * Sorts the @todo list - do not override or modify this function * @access private * @uses _sortTodos passed to {@link usort()} to sort the todo list */ function sortTodos() { phpDocumentor_out("\nSorting @todo list..."); flush(); foreach($this->todoList as $package => $r) usort($this->todoList[$package],array($this, '_sortTodos')); phpDocumentor_out("done\n"); } /** @access private */ function _sortTodos($a, $b) { return strnatcasecmp($a[0]->name, $b[0]->name); } /** * Sorts all indexes - do not override or modify this function * @uses $leftindex based on the value of leftindex, sorts link arrays * @uses $class_elements sorts with {@link compareLink} * @uses $page_elements sorts with {@link compareLink} * @uses $define_elements sorts with {@link compareLink} * @uses $global_elements sorts with {@link compareLink} * @uses $function_elements sorts with {@link compareLink} * @uses $elements sorts with {@link elementCmp} * @uses $pkg_elements sorts with {@link elementCmp} after sorting by * package/subpackage alphabetically * @access private */ function sortIndexes() { phpDocumentor_out("\nSorting Indexes..."); flush(); uksort($this->elements,'strnatcasecmp'); if ($this->leftindex['classes']) { foreach($this->class_elements as $package => $o1) { foreach($o1 as $subpackage => $links) { usort($this->class_elements[$package][$subpackage],array($this,'compareLink')); } } } if ($this->leftindex['pages']) { foreach($this->page_elements as $package => $o1) { uksort($this->page_elements[$package],'strnatcasecmp'); foreach($o1 as $subpackage => $links) { usort($this->page_elements[$package][$subpackage],array($this,'compareLink')); } } } if ($this->leftindex['defines']) { foreach($this->define_elements as $package => $o1) { uksort($this->define_elements[$package],'strnatcasecmp'); foreach($o1 as $subpackage => $links) { usort($this->define_elements[$package][$subpackage],array($this,'compareLink')); } } } if ($this->leftindex['globals']) { foreach($this->global_elements as $package => $o1) { uksort($this->global_elements[$package],'strnatcasecmp'); foreach($o1 as $subpackage => $links) { usort($this->global_elements[$package][$subpackage],array($this,'compareLink')); } } } if ($this->leftindex['functions']) { foreach($this->function_elements as $package => $o1) { uksort($this->function_elements[$package],'strnatcasecmp'); foreach($o1 as $subpackage => $links) { usort($this->function_elements[$package][$subpackage],array($this,'compareLink')); } } } foreach($this->elements as $letter => $nothuing) { uasort($this->elements[$letter],array($this,"elementCmp")); } foreach($this->pkg_elements as $package => $els) { uksort($this->pkg_elements[$package],'strnatcasecmp'); foreach($this->pkg_elements[$package] as $subpackage => $els) { if (empty($els)) continue; uksort($this->pkg_elements[$package][$subpackage],'strnatcasecmp'); foreach($els as $letter => $yuh) { usort($this->pkg_elements[$package][$subpackage][$letter],array($this,"elementCmp")); } } } phpDocumentor_out("done\n"); flush(); } /** * sorts {@link $page_contents} by element type as well as alphabetically * @see $sort_page_contents_by_element_type */ function sortPageContentsByElementType(&$pages) { foreach($this->page_contents as $package => $els) { foreach($this->page_contents[$package] as $subpackage => $els) { if (empty($els)) continue; foreach($this->page_contents[$package][$subpackage] as $path => $stuff) { if (!count($pages[$path]->elements)) continue; usort($pages[$path]->elements,array($this,'eltypecmp')); usort($this->page_contents[$package][$subpackage][$path],array($this,'eltypecmp')); if (isset($this->page_contents[$package][$subpackage][$path][0])) $this->page_contents[$package][$subpackage][$path]['###main'] = $this->page_contents[$package][$subpackage][$path][0]; unset($this->page_contents[$package][$subpackage][$path][0]); } } } } /** * @access private * @see Converter::sortIndexes() */ function compareLink($a, $b) { return strnatcasecmp($a->name,$b->name); } /** * @access private * @see Converter::sortPageContentsByElementType() */ function eltypecmp($a, $b) { if ($a->type == 'page') return -1; if ($b->type == 'page') return 1; return strnatcasecmp($a->type.$a->name,$b->type.$b->name); } /** * does a nat case sort on the specified second level value of the array * * @param mixed $a * @param mixed $b * @return int * @access private */ function elementCmp ($a, $b) { return strnatcasecmp($a->getName(), $b->getName()); } /** * Used to stop conversion of @ignored or private @access classes * @uses $killclass sets killclass based on the value of {@link Classes::$killclass} * and {@link $package_output} * @access private */ function checkKillClass($class, $path) { $this->killclass = false; if (isset($this->classes->killclass[$class]) && isset($this->classes->killclass[$class][$path])) $this->killclass = true; if ($this->package_output) { $a = $this->classes->getClass($class, $path); if (!in_array($a->docblock->package,$this->package_output)) $this->killclass = true; } if (PHPDOCUMENTOR_DEBUG && $this->killclass) debug("$class $path killed"); return $this->killclass; } /** * @param abstractLink descendant of abstractLink * @param array|parserTag list of @todos|@todo tag * @access private */ function addTodoLink($link, $todos) { $this->todoList[$link->package][] = array($link, $todos); } /** * Adds all elements to the {@link $elements, $pkg_elements, $links}, * {@link $linkswithfile} and left indexes - Do not modify or override * @access private * @param parserBase any documentable element descendant of parserBase * except parserTutorial * @param false|parserPage only used to add a {@link parserPage} if the * $element passed is a parserPage * @staticvar string path of current page, used for {@link $page_contents} setup */ function addElement(&$element,$pageel=false) { static $curpath = ''; if ($this->package_output) { if (!in_array($this->package, $this->package_output)) return; } if ($pageel && phpDocumentor_get_class($pageel) == 'parserdata') { if (isset($pageel->docblock) && phpDocumentor_get_class($pageel->docblock) == 'parserdocblock') { $a = $pageel->docblock->getKeyword('todo'); if ($a) { $this->addTodoLink($this->addLink($element),$a); } } } if (isset($element->docblock)) { $a = $element->docblock->getKeyword('access'); if (is_object($a)) $a = $a->getString(); if (!$this->parseprivate && ($a == 'private')) return; $a = $element->docblock->getKeyword('todo'); if ($a) { $this->addTodoLink($this->addLink($element),$a); } } $i = 0; switch($element->type) { case 'page' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->package][$element->subpackage]['page'][$element->getPath()][] = $pageel; } $link = $this->addLink($element); $curpath = $element->getPath(); if ($this->leftindex['pages']) $this->page_elements[$element->package][$element->subpackage][] = $link; $this->page_contents[$element->package][$element->subpackage][$curpath]['###main'] = $link; break; case 'class' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['class'][$this->class][] = $element; } $link = $this->addLink($element); if ($this->leftindex['classes']) $this->class_elements[$element->docblock->package][$element->docblock->subpackage][] = $link; $this->class_contents[$element->docblock->package][$element->docblock->subpackage][$this->class]['###main'] = $link; break; case 'include' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['page'][$curpath][] = $element; } $link = $this->addLink($element); break; case 'define' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['page'][$curpath][] = $element; } $link = $this->addLink($element); if ($this->leftindex['defines']) $this->define_elements[$element->docblock->package][$element->docblock->subpackage][] = $link; $this->page_contents[$element->docblock->package][$element->docblock->subpackage][$curpath][] = $link; break; case 'global' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['page'][$curpath][] = $element; } $link = $this->addLink($element); $i++; if ($this->leftindex['globals']) $this->global_elements[$element->docblock->package][$element->docblock->subpackage][] = $link; $this->page_contents[$element->docblock->package][$element->docblock->subpackage][$curpath][] = $link; break; case 'var' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['class'][$this->class][] = $element; } $link = $this->addLink($element); $i++; $this->class_contents[$element->docblock->package][$element->docblock->subpackage][$this->class][] = $link; break; case 'const' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['class'][$this->class][] = $element; } $link = $this->addLink($element); $i++; $this->class_contents[$element->docblock->package][$element->docblock->subpackage][$this->class][] = $link; break; case 'method' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['class'][$this->class][] = $element; } $link = $this->addLink($element); $this->class_contents[$element->docblock->package][$element->docblock->subpackage][$this->class][] = $link; break; case 'function' : if ($this->sort_absolutely_everything) { $this->package_elements[$element->docblock->package][$element->docblock->subpackage]['page'][$curpath][] = $element; } $link = $this->addLink($element); if ($this->leftindex['functions']) $this->function_elements[$element->docblock->package][$element->docblock->subpackage][] = $link; $this->page_contents[$element->docblock->package][$element->docblock->subpackage][$curpath][] = $link; break; default : break; } if ($element->getType() != 'include') { if ($element->getType() == 'var' || $element->getType() == 'method'|| $element->getType() == 'const') { $this->links[$this->package][$this->subpackage][$element->getType()][$element->class][$element->getName()] = $link; $this->linkswithfile[$this->package][$this->subpackage][$element->getType()][$element->getPath()][$element->class][$element->getName()] = $link; } else { if ($element->type == 'page') { $this->links[$this->package][$this->subpackage][$element->getType()][$element->getFile()] = $link; $this->linkswithfile[$this->package][$this->subpackage][$element->getType()][$element->getPath()][$element->getFile()] = $link; } else { $this->links[$this->package][$this->subpackage][$element->getType()][$element->getName()] = $link; $this->linkswithfile[$this->package][$this->subpackage][$element->getType()][$element->getPath()][$element->getName()] = $link; } } } if ($element->type == 'page') { $this->elements[substr(strtolower($element->getFile()),$i,1)][] = $element; $this->pkg_elements[$this->package][$this->subpackage][substr(strtolower($element->getFile()),$i,1)][] = $element; } else { $this->elements[substr(strtolower($element->getName()),$i,1)][] = $element; $this->pkg_elements[$this->package][$this->subpackage][substr(strtolower($element->getName()),$i,1)][] = $element; } } /** * returns an abstract link to element. Do not modify or override * * This method should only be called in process of Conversion, unless * $element is a parserPage, or $page is set to true, and $element is * not a parserPage * @return abstractLink abstractLink descendant * @access private * @param parserElement element to add a new link (descended from * {@link abstractLink})to the {@link $links} array * @param string classname for elements that are class-based (this may be * deprecated in the future, as the classname * should be contained within the element. if $element is a * page, this parameter is a package name * @param string subpackage name for page elements */ function addLink(&$element,$page = false) { if ($page) { // create a fake parserPage to extract the fileAlias for this link $fakepage = new parserPage; $fakepage->setPath($element->getPath()); $fakepage->setFile(basename($element->getPath())); $this->curname = $this->getPageName($fakepage); } switch($element->type) { case 'function': $x = new functionLink; $x->addLink($element->getPath(), $this->curname, $element->name, $element->docblock->package, $element->docblock->subpackage, $element->docblock->category); return $x; break; case 'define': $x = new defineLink; $x->addLink($element->getPath(), $this->curname, $element->name, $element->docblock->package, $element->docblock->subpackage, $element->docblock->category); return $x; break; case 'global': $x = new globalLink; $x->addLink($element->getPath(), $this->curname, $element->name, $element->docblock->package, $element->docblock->subpackage, $element->docblock->category); return $x; break; case 'class': $x = new classLink; $x->addLink($element->getPath(), $this->curname, $element->name, $element->docblock->package, $element->docblock->subpackage, $element->docblock->category); return $x; break; case 'method': $x = new methodLink; $x->addLink($this->class, $element->getPath(), $this->curname, $element->name, $element->docblock->package, $element->docblock->subpackage, $element->docblock->category); return $x; break; case 'var': $x = new varLink; $x->addLink($this->class, $element->getPath(), $this->curname, $element->name, $element->docblock->package, $element->docblock->subpackage, $element->docblock->category); return $x; break; case 'const': $x = new constLink; $x->addLink($this->class, $element->getPath(), $this->curname, $element->name, $element->docblock->package, $element->docblock->subpackage, $element->docblock->category); return $x; break; case 'page': $x = new pageLink; $x->addLink($element->getPath(),$this->getPageName($element),$element->file,$element->package, $element->subpackage, $element->category); return $x; break; } } /** * Return a tree of all classes that extend this class * * The data structure returned is designed for a non-recursive algorithm, * and is somewhat complex. * In most cases, the array returned is: * * <pre> * array('#root' => * array('link' => {@link classLink} to $class, * 'parent' => false, * 'children' => array(array('class' => 'childclass1', * 'package' => 'child1package'), * array('class' => 'childclass2', * 'package' => 'child2package'),... * ) * ), * 'child1package#childclass1' => * array('link' => {@link classLink} to childclass1, * 'parent' => '#root', * 'children' => array(array('class' => 'kidclass', * 'package' => 'kidpackage'),... * ) * ), * 'kidpackage#kidclass' => * array('link' => {@link classLink} to kidclass, * 'parent' => 'child1package#childclass1', * 'children' => array() // no children * ), * .... * ) *</pre> * * To describe this format using language, every class in the tree has an * entry in the first level of the array. The index for all child * classes that extend the root class is childpackage#childclassname. * Each entry in the array has 3 elements: link, parent, and children. * <ul> * <li>link - a {@link classLink} to the current class</li> * <li>parent - a {@link classLink} to the class's parent, or false (except for one special case described below)</li> * <li>children - an array of arrays, each entry has a 'class' and 'package' index to the child class, * used to find the entry in the big array</li> * </ul> * * special cases are when the #root class has a parent in another package, * or when the #root class extends a class not found * by phpDocumentor. In the first case, parent will be a * classLink to the parent class. In the second, parent will be the * extends clause, as in: * <code> * class X extends Y * { * ... * } * </code> * in this case, the #root entry will be array('link' => classLink to X, 'parent' => 'Y', children => array(...)) * * The fastest way to design a method to process the array returned * is to copy HTMLframesConverter::getRootTree() into * your converter and to modify the html to whatever output format you are going to use * @see HTMLframesConverter::getRootTree() * @param string class name * @param string * @param string * @return array Format: see docs */ function getSortedClassTreeFromClass($class,$package,$subpackage) { $my_tree = array(); $root = $this->classes->getClassByPackage($class,$package); if (!$root) return false; $class_children = $this->classes->getDefiniteChildren($class,$root->curfile); if (!$class_children) { // special case: parent class is found, but is not part of this package, class has no children if (is_array($root->parent)) { $x = $root->getParent($this); if ($x->docblock->package != $package) { $v = Converter::getClassLink($root->getName(),$package,$root->getPath()); return array('#root' => array('link' => $v,'parent' => Converter::getClassLink($x->getName(),$x->docblock->package,$x->getPath()), 'children' => array())); } } else { // class has normal situation, no children if (is_string($root->getParent($this))) return array('#root' => array('link' => Converter::getClassLink($root->getName(),$package,$root->getPath()), 'parent' => $root->getExtends(),'children' => array())); else return array('#root' => array('link' => Converter::getClassLink($root->getName(),$package,$root->getPath()), 'parent' => false, 'children' => array())); } } // special case: parent class is found, but is not part of this package, class has children if (is_array($root->parent)) { $x = $root->getParent($this); if ($x->docblock->package != $package) { $v = Converter::getClassLink($root->getName(),$package,$root->getPath()); $my_tree = array('#root' => array('link' => $v, 'parent' => Converter::getClassLink($x->getName(),$x->docblock->package,$x->getPath()), 'children' => array())); } else { } } else $my_tree = array('#root' => array('link' => Converter::getClassLink($root->getName(),$package,$root->getPath()), 'parent' => false, 'children' => array())); // location of tree walker $cur = '#root'; $lastcur = array(array(false,0)); $childpos = 0; if (isset($class_children)) { do { if (!$class_children) { list($cur, $childpos) = array_pop($lastcur); if (isset($my_tree[$cur]['children'][$childpos + 1])) { array_push($lastcur, array($cur, $childpos + 1)); $par = $cur; $cur = $my_tree[$cur]['children'][$childpos + 1]; $x = $this->classes->getClassByPackage($cur['class'],$cur['package']); $childpos = 0; $cur = $cur['package'] . '#' . $cur['class']; $my_tree[$cur]['link'] = Converter::getClassLink($x->getName(),$x->docblock->package,$x->getPath()); $my_tree[$cur]['parent'] = $par; $my_tree[$cur]['children'] = array(); $class_children = $this->classes->getDefiniteChildren($x->getName(), $x->curfile); continue; } else { $class_children = false; continue; } } foreach($class_children as $chileclass => $chilefile) { $ch = $this->classes->getClass($chileclass,$chilefile); $my_tree[$cur]['children'][] = array('class' => $ch->getName(), 'package' => $ch->docblock->package); } usort($my_tree[$cur]['children'],'rootcmp'); if (isset($my_tree[$cur]['children'][$childpos])) { array_push($lastcur, array($cur, $childpos)); $par = $cur; $cur = $my_tree[$cur]['children'][$childpos]; $x = $this->classes->getClassByPackage($cur['class'],$cur['package']); $cur = $cur['package'] . '#' . $cur['class']; $my_tree[$cur]['link'] = Converter::getClassLink($x->getName(),$x->docblock->package,$x->getPath()); $my_tree[$cur]['parent'] = $par; $my_tree[$cur]['children'] = array(); $childpos = 0; $class_children = $this->classes->getDefiniteChildren($x->getName(), $x->curfile); } else { list($cur, $childpos) = array_pop($lastcur); } } while ($cur); } return $my_tree; } /** * do not override * @return bool true if a link to this class exists in package $package and subpackage $subpackage * @param string $expr class name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedClass($expr,$package,$subpackage,$file=false) { if ($file) return isset($this->linkswithfile[$package][$subpackage]['class'][$file][$expr]); return isset($this->links[$package][$subpackage]['class'][$expr]); } /** * do not override * @return bool true if a link to this function exists in package $package and subpackage $subpackage * @param string $expr function name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedFunction($expr,$package,$subpackage,$file=false) { if ($file) return isset($this->linkswithfile[$package][$subpackage]['function'][$file][$expr]); return isset($this->links[$package][$subpackage]['function'][$expr]); } /** * do not override * @return bool true if a link to this define exists in package $package and subpackage $subpackage * @param string $expr define name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedDefine($expr,$package,$subpackage,$file=false) { if ($file) return isset($this->linkswithfile[$package][$subpackage]['define'][$file][$expr]); return isset($this->links[$package][$subpackage]['define'][$expr]); } /** * do not override * @return bool true if a link to this define exists in package $package and subpackage $subpackage * @param string $expr define name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedGlobal($expr,$package,$subpackage,$file=false) { if ($file) return isset($this->linkswithfile[$package][$subpackage]['global'][$file][$expr]); return isset($this->links[$package][$subpackage]['global'][$expr]); } /** * do not override * @return bool true if a link to this procedural page exists in package $package and subpackage $subpackage * @param string $expr procedural page name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedPage($expr,$package,$subpackage,$path=false) { if ($path) return isset($this->linkswithfile[$package][$subpackage]['page'][$path][$expr]); return isset($this->links[$package][$subpackage]['page'][$expr]); } /** * do not override * @return bool true if a link to this method exists in package $package, subpackage $subpackage and class $class * @param string $expr method name * @param string $class class name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedMethod($expr,$package,$subpackage,$class,$file=false) { if ($file) return isset($this->linkswithfile[$package][$subpackage]['method'][$file][$class][$expr]); return isset($this->links[$package][$subpackage]['method'][$class][$expr]); } /** * do not override * @return bool true if a link to this method exists in package $package, subpackage $subpackage and class $class * @param string $expr var name * @param string $class class name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedVar($expr,$package,$subpackage,$class,$file=false) { if ($file) return isset($this->linkswithfile[$package][$subpackage]['var'][$file][$class][$expr]); return isset($this->links[$package][$subpackage]['var'][$class][$expr]); } /** * do not override * @return bool true if a link to this method exists in package $package, subpackage $subpackage and class $class * @param string $expr constant name * @param string $class class name * @param string $package package to search in * @param string $subpackage subpackage to search in * @access private */ function isLinkedConst($expr,$package,$subpackage,$class,$file=false) { if ($file) return isset($this->linkswithfile[$package][$subpackage]['const'][$file][$class][$expr]); return isset($this->links[$package][$subpackage]['const'][$class][$expr]); } /** * return false or a {@link classLink} to $expr * @param string $expr class name * @param string $package package name * @return mixed returns a {@link classLink} or false if the element is not found in package $package * @see classLink */ function getClassLink($expr,$package,$file=false, $text = false) { if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedClass($expr,$package,$subpackage,$file)) { if ($file) { return $this->linkswithfile[$package][$subpackage]['class'][$file][$expr]; } return $this->links[$package][$subpackage]['class'][$expr]; } } return false; } /** * return false or a {@link functionLink} to $expr * @param string $expr function name * @param string $package package name * @return mixed returns a {@link functionLink} or false if the element is not found in package $package * @see functionLink */ function getFunctionLink($expr,$package,$file=false, $text = false) { if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedFunction($expr,$package,$subpackage,$file)) { if ($file) { return $this->linkswithfile[$package][$subpackage]['function'][$file][$expr]; } return $this->links[$package][$subpackage]['function'][$expr]; } } return false; } /** * return false or a {@link defineLink} to $expr * @param string $expr constant name * @param string $package package name * @return mixed returns a {@link defineLink} or false if the element is not found in package $package * @see defineLink */ function getDefineLink($expr,$package,$file=false, $text = false) { if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedDefine($expr,$package,$subpackage,$file)) { if ($file) { return $this->linkswithfile[$package][$subpackage]['define'][$file][$expr]; } return $this->links[$package][$subpackage]['define'][$expr]; } } return false; } /** * return false or a {@link globalLink} to $expr * @param string $expr global variable name (with leading $) * @param string $package package name * @return mixed returns a {@link defineLink} or false if the element is not found in package $package * @see defineLink */ function getGlobalLink($expr,$package,$file=false, $text = false) { if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedGlobal($expr,$package,$subpackage,$file)) { if ($file) { return $this->linkswithfile[$package][$subpackage]['global'][$file][$expr]; } return $this->links[$package][$subpackage]['global'][$expr]; } } return false; } /** * return false or a {@link pageLink} to $expr * @param string $expr procedural page name * @param string $package package name * @return mixed returns a {@link pageLink} or false if the element is not found in package $package * @see pageLink */ function getPageLink($expr,$package,$path = false, $text = false, $packages = false) { if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedPage($expr,$package,$subpackage,$path)) { if ($path) { return $this->linkswithfile[$package][$subpackage]['page'][$path][$expr]; } return $this->links[$package][$subpackage]['page'][$expr]; } } return false; } /** * return false or a {@link methodLink} to $expr in $class * @param string $expr method name * @param string $class class name * @param string $package package name * @return mixed returns a {@link methodLink} or false if the element is not found in package $package, class $class * @see methodLink */ function getMethodLink($expr,$class,$package,$file=false, $text = false) { $expr = trim($expr); $class = trim($class); if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedMethod($expr,$package,$subpackage,$class,$file)) { if ($file) { return $this->linkswithfile[$package][$subpackage]['method'][$file][$class][$expr]; } return $this->links[$package][$subpackage]['method'][$class][$expr]; } } return false; } /** * return false or a {@link varLink} to $expr in $class * @param string $expr var name * @param string $class class name * @param string $package package name * @return mixed returns a {@link varLink} or false if the element is not found in package $package, class $class * @see varLink */ function getVarLink($expr,$class,$package,$file=false, $text = false) { $expr = trim($expr); $class = trim($class); if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedVar($expr,$package,$subpackage,$class,$file)) { if ($file) { return $this->linkswithfile[$package][$subpackage]['var'][$file][$class][$expr]; } return $this->links[$package][$subpackage]['var'][$class][$expr]; } } return false; } /** * return false or a {@link constLink} to $expr in $class * @param string $expr constant name * @param string $class class name * @param string $package package name * @return mixed returns a {@link varLink} or false if the element is not found in package $package, class $class * @see constLink */ function getConstLink($expr,$class,$package,$file=false, $text = false) { $expr = trim($expr); $class = trim($class); if (!isset($this->links[$package])) return false; foreach($this->links[$package] as $subpackage => $notused) { if ($this->isLinkedConst($expr,$package,$subpackage,$class,$file)) { if ($file) { return $this->linkswithfile[$package][$subpackage]['const'][$file][$class][$expr]; } return $this->links[$package][$subpackage]['const'][$class][$expr]; } } return false; } /** * The meat of the @tutorial tag and inline {@}tutorial} tag * * Take a string and return an abstract link to the tutorial it represents. * Since tutorial naming literally works like the underlying filesystem, the * way to reference the tutorial is similar. Tutorials are located in a * subdirectory of any directory parsed, which is named 'tutorials/' (we * try to make things simple when we can :). They are further organized by * package and subpackage as: * * tutorials/package/subpackage * * and the files are named *.cls, *.pkg, or *.proc, and so a link to a tutorial * named file.cls can be referenced (depending on context) as any of: * * <code> * * @tutorial package/subpackage/file.cls * * @tutorial package/file.cls * * @tutorial file.cls * </code> * * The first case will only be needed if file.cls exists in both the current * package, in anotherpackage/file.cls and in anotherpackage/subpackage/file.cls * and you wish to reference the one in anotherpackage/subpackage. * The second case is only needed if you wish to reference file.cls in another * package and it is unique in that package. the third will link to the first * file.cls it finds using this search method: * * <ol> * <li>current package/subpackage</li> * <li>all other subpackages of current package</li> * <li>parent package, if this package has classes that extend classes in * another package</li> * <li>all other packages</li> * </ol> * @return tutorialLink|string returns either a link, or the original text, if not found * @param string the original expression * @param string package to look in first * @param string subpackage to look in first * @param array array of package names to search in if not found in parent packages. * This is used to limit the search, phpDocumentor automatically searches * all packages * @since 1.2 */ function getTutorialLink($expr, $package = false, $subpackage = false, $packages = false) { // is $expr a comma-delimited list? if (strpos($expr,',')) { $a = explode(',',$expr); $b = array(); for($i=0;$i<count($a);$i++) { // if so return each component with a link $b[] = Converter::getTutorialLink(trim($a[$i])); } return $b; } $subsection = ''; if (strpos($expr,'#')) { $a = explode('#',$expr); $org = $expr; $expr = $a[0]; $subsection = $a[1]; } if (strpos($expr,'/')) { $a = explode('/',$expr); if (count($a) == 3) { return Converter::getTutorialLink($a[2],$a[0],$a[1],array()); } if (count($a) == 2) { return Converter::getTutorialLink($a[1],$a[0],false,array()); } } if (!$package) $package = $this->package; if (!$subpackage) $subpackage = $this->subpackage; if (!isset($this->all_packages[$package])) return $expr; elseif (isset($packages[$package])) unset($packages[$package]); $ext = pathinfo($expr, PATHINFO_EXTENSION); if (isset($this->tutorials[$package][$subpackage][$ext][$expr])) { $a = $this->tutorials[$package][$subpackage][$ext][$expr]; $link = new tutorialLink; $link->addLink($subsection,$a->path,$a->name,$a->package,$a->subpackage,$a->getTitle($this,$subsection)); return $link; } do { if (!is_array($packages)) { $packages = $this->all_packages; if (isset($packages[$package])) unset($packages[$package]); } if (isset($this->tutorials[$package])) { if (isset($this->tutorials[$package][$subpackage][$ext][$expr])) { $a = $this->tutorials[$package][$subpackage][$ext][$expr]; $link = new tutorialLink; $link->addLink($subsection,$a->path,$a->name,$a->package,$a->subpackage,$a->getTitle($this)); return $link; } else { foreach($this->tutorials[$package] as $subpackage => $stuff) { if (isset($stuff[$ext][$expr])) { $a = $stuff[$ext][$expr]; $link = new tutorialLink; $link->addLink($subsection,$a->path,$a->name,$a->package,$a->subpackage,$a->getTitle($this)); return $link; } } } } // try other packages // look in parent package first, if found if (isset($this->package_parents[$package])) { $p1 = $package; $package = $this->package_parents[$package]; } else { // no parent package, so start with the first one that's left list($package,) = @each($packages); } if ($package) { if (isset($packages[$package])) unset($packages[$package]); } } while (count($packages) || $package); addWarning(PDERROR_TUTORIAL_NOT_FOUND,$expr); return $expr; } /** * The meat of the @see tag and inline {@}link} tag * * $expr is a string with many allowable formats: * <ol> * <li>proceduralpagename.ext</li> * <li>constant_name</li> * <li>classname::function()</li> * <li>classname::constantname</li> (new 1.2.4) * <li>classname::$variablename</li> * <li>classname</li> * <li>function functionname()</li> * <li>global $globalvarname</li> * <li>packagename#expr where expr is any of the above</li> * </ol> * * New in version 1.1, you can explicitly specify a package to link to that * is different from the current package. Use the # operator * to specify a new package, as in tests#bug-540368.php (which should appear * as a link like: "{@link tests#bug-540368.php}"). This * example links to the procedural page bug-540368.php in package * tests. Also, the "function" operator is now used to specifically * link to a function instead of a method in the current class. * * <code> * class myclass * { * // from inside the class definition, use "function conflict()" to refer to procedural function "conflict()" * function conflict() * { * } * } * * function conflict() * { * } * </code> * * If classname:: is not present, and the see tag is in a documentation * block within a class, then the function uses the classname to * search for $expr as a function or variable within classname, or any of its parent classes. * given an $expr without '$', '::' or '()' getLink first searches for * classes, procedural pages, constants, global variables, and then searches for * methods and variables within the default class, and finally for any function * * @param string $expr expression to search for a link * @param string $package package to start searching in * @param array $packages list of all packages to search in * @return mixed getLink returns a descendant of {@link abstractLink} if it finds a link, otherwise it returns a string * @see getPageLink(), getDefineLink(), getVarLink(), getFunctionLink(), getClassLink() * @see pageLink, functionLink, defineLink, classLink, methodLink, varLink */ function &getLink($expr, $package = false, $packages = false) { // is $expr a comma-delimited list? if (strpos($expr,',')) { $a = explode(',',$expr); $b = array(); for($i=0;$i<count($a);$i++) { // if so return each component with a link $b[] = Converter::getLink(trim($a[$i])); } return $b; } if (strpos($expr,'#')) { $a = explode('#',$expr); if (count($a) == 2) { // can have exactly 1 package override, otherwise it's ignored // feature 564991, link to php manual if ($a[0] == 'PHP_MANUAL') return 'http://www.php.net/'.$a[1]; return Converter::getLink($a[1],$a[0],array()); } } $a = &$this->_getLink($expr, $package, $packages); return $a; } /** * @access private */ function &_getLink($expr, $package = false, $packages = false) { if (!$package) $package = $this->package; // if (!isset($this->all_packages[$package])) return $expr; elseif (isset($packages[$package])) unset($packages[$package]); $links = &$this->links; $class = $this->class; if (strpos($expr,'function ') === 0) { // asking for a function, not a method if ($test = Converter::getFunctionLink(str_replace('function ','',str_replace('()','',$expr)), $package)) return $test; else return $expr; } if (strpos($expr,'global ') === 0) { // asking for a global variable if ($test = Converter::getGlobalLink(str_replace('global ','',$expr), $package)) return $test; else return $expr; } if (strpos($expr,'object ') === 0) { // asking for a class if ($test = Converter::getClassLink(str_replace('object ','',$expr), $package)) return $test; else return $expr; } if (strpos($expr,'constant ') === 0) { // asking for a class if ($test = Converter::getDefineLink(str_replace('constant ','',$expr), $package)) return $test; else return $expr; } // are we in a class? if ($class) { // is $expr simply a word? see if it is the class if (trim($expr) == $class) { if ($test = Converter::getClassLink(trim(str_replace('object ','',$expr)),$package)) return $test; } // if not, check to see if it is a method or variable of this class tree if (!strpos($expr,'::')) { // if get is neither get() nor $get, assume get is a function, add () to make get() if (strpos($expr,'$') !== 0 && !strpos($expr,'()')) //$get = $get.'()'; { if ($a = $this->getLinkMethod($expr,$class,$package)) return $a; if ($a = $this->getLinkConst($expr,$class,$package)) return $a; if ($a = $this->getLinkVar('$'.$expr,$class,$package)) return $a; } if (strpos($expr,'()')) if ($a = $this->getLinkMethod($expr,$class,$package)) return $a; if (is_numeric(strpos($expr,'$'))) if ($a = $this->getLinkVar($expr,$class,$package)) return $a; } } if ($test = Converter::getClassLink(trim(str_replace('object ','',$expr)),$package)) return $test; if ($test = Converter::getPageLink(trim($expr),$package)) return $test; if ($test = Converter::getDefineLink(trim($expr),$package)) return $test; if ($test = Converter::getGlobalLink(trim($expr),$package)) return $test; // if (strpos($expr,'.')) // package specified if (!is_array($packages)) { $packages = $this->all_packages; } do { if (isset($packages[$package])) unset($packages[$package]); if ($test = Converter::getClassLink(str_replace('object ','',$expr),$package)) return $test; if ($test = Converter::getPageLink($expr,$package)) return $test; if ($test = Converter::getDefineLink($expr,$package)) return $test; if ($test = Converter::getGlobalLink($expr,$package)) return $test; // is $expr in class::method() or class::$variable format? if (strpos($expr,'function ') === 0) { // asking for a function, not a method if ($test = Converter::getFunctionLink(str_replace('function','',str_replace('()','',$expr)), $package)) return $test; else return $expr; } $test = $this->_getDoubleColon($expr, $package, $packages, $class, $links); if (!is_string($test)) return $test; if (strpos($test, 'parent::') === 0) return $test; // $expr does not have :: if (is_numeric(@strpos('$',$expr))) { // default to current class, whose name is contained in $this->render->parent if ($test = Converter::getVarLink($expr, $class, $package)) return $test; } // $expr is a function? (non-method) if (@strpos($expr,'()')) { // otherwise, see if it is a method if ($class) { if ($test = Converter::getMethodLink(str_replace('()','',$expr), $class, $package)) return $test; } // extract the function name, use it to retrieve the file that the function is in // $page = $this->func_page[str_replace('function ','',str_replace('()','',$expr))]; // return the link if ($test = Converter::getFunctionLink(str_replace('function ','',str_replace('()','',$expr)), $package)) return $test; } // $expr is just a word. First, test to see if it is a function of the current package if ($test = Converter::getFunctionLink(str_replace('function ','',str_replace('()','',$expr)), $package)) return $test; // try other packages // look in parent package first, if found if (isset($this->package_parents[$package]) && in_array($this->package_parents[$package], $packages)) { $p1 = $package; $package = $this->package_parents[$package]; if ($package) { if (isset($packages[$package])) unset($packages[$package]); } continue; } // no parent package, so start with the first one that's left $package = @array_shift(@array_keys($packages)); if ($package && isset($packages[$package])) { unset($packages[$package]); } } while (count($packages) || $package); $funcs = get_defined_functions(); // feature 564991, link to php manual if (in_array(str_replace(array('(',')'),array('',''),$expr),$funcs['internal'])) { return 'http://www.php.net/'.str_replace(array('(',')'),array('',''),$expr); } // no links found return $expr; } /** * Split up getLink to make it easier to debug * @access private */ function _getDoubleColon(&$expr, &$package, &$packages, $class, $links) { if (@strpos($expr,'::')) { $class_method = explode('::',$expr); if ($class_method[0] == 'parent') { // can only have parent in the same package as the class! subtle bug $package = $this->package; $packages = array(); $cl = $this->classes->getClassByPackage($class,$package); if (!$cl) { // this is possible if an example file has parent::method() return $expr; } $par = $cl->getParent($this); $phpparent = false; if (is_object($par)) { $package = $par->docblock->package; $phpparent = $par->getName(); } else { addWarning(PDERROR_CLASS_PARENT_NOT_FOUND,$class,$package,$class_method[1]); return $expr; } if ($phpparent) $class_method[0] = $phpparent; } if (strpos($class_method[1],'()')) { // strip everything but the function name, return a link if ($test = Converter::getMethodLink(str_replace('()','',$class_method[1]), $class_method[0], $package)) return $test; } if ($test = Converter::getVarLink($class_method[1], $class_method[0], $package)) return $test; if ($test = Converter::getConstLink($class_method[1], $class_method[0], $package)) return $test; } return $expr; } /** * cycle through parent classes to retrieve a link to a method * do not use or override, used by getLink * @access private */ function &getLinkMethod($expr, $class, $package) { $links = &$this->links; do { // is $expr in class::method() or class::$variable format? if (@strpos($expr,'::')) { $class_method = explode('::',$expr); if ($class_method[0] == 'parent') { $cl = $this->classes->getClassByPackage($class,$package); $par = $cl->getParent($this); $phpparent = false; if (is_object($par)) { $package = $par->docblock->package; $phpparent = $par->getName(); } else addWarning(PDERROR_CLASSPARENT_NOTFOUND,$class,$package,$class_method[1]); if ($phpparent) $class_method[0] = $phpparent; } else { $cl = $this->classes->getClassByPackage($class,$package); } if (strpos($class_method[1],'()')) { // strip everything but the function name, return a link if ($test = Converter::getMethodLink(str_replace('function ','',str_replace('()','',$class_method[1])), $class_method[0], $package)) return $test; } } if ($test = Converter::getMethodLink(str_replace('()','',$expr), $class, $package)) return $test; $cl = $this->classes->getClassByPackage($class,$package); if ($cl) { $par = $cl->getParent($this); if (is_object($par)) { $package = $par->docblock->package; $class = $par->getName(); } else $class = $par; } else $class = false; } while ($class); // no links found return false; } /** * cycle through parent classes to retrieve a link to a var * do not use or override, used by getLink * @access private */ function &getLinkVar($expr, $class, $package) { $links = &$this->links; do { // is $expr in class::method() or class::$variable format? if (@strpos($expr,'::')) { $class_method = explode('::',$expr); if ($class_method[0] == 'parent') { $cl = $this->classes->getClassByPackage($class,$package); $phpparent = false; $par = $cl->getParent($this); if (is_object($par)) { $package = $par->docblock->package; $phpparent = $par->getName(); } else addWarning(PDERROR_CLASSPARENT_NOTFOUND,$class,$package,$class_method[1]); if ($phpparent) $class_method[0] = $phpparent; } else { $cl = $this->classes->getClassByPackage($class,$package); } if ($test = Converter::getVarLink($class_method[1], $class_method[0], $package)) return $test; if ($test = Converter::getVarLink('$'.$class_method[1], $class_method[0], $package)) return $test; } if ($test = Converter::getVarLink($expr, $class, $package)) return $test; if ($test = Converter::getVarLink('$'.$expr, $class, $package)) return $test; $cl = $this->classes->getClassByPackage($class,$package); if ($cl) { $par = $cl->getParent($this); if (is_object($par)) { $package = $par->docblock->package; $class = $par->getName(); } else $class = $par; } else $class = false; } while ($class); // no links found $class = false; return $class; } /** * cycle through parent classes to retrieve a link to a class constant * do not use or override, used by getLink * @access private * @since 1.2.4 */ function &getLinkConst($expr, $class, $package) { $links = &$this->links; do { // is $expr in class::method() or class::$variable format? if (@strpos($expr,'::')) { $class_method = explode('::',$expr); if ($class_method[0] == 'parent') { $cl = $this->classes->getClassByPackage($class,$package); $phpparent = false; $par = $cl->getParent($this); if (is_object($par)) { $package = $par->docblock->package; $phpparent = $par->getName(); } else addWarning(PDERROR_CLASSPARENT_NOTFOUND,$class,$package,$class_method[1]); if ($phpparent) $class_method[0] = $phpparent; } else { $cl = $this->classes->getClassByPackage($class,$package); } if ($test = Converter::getConstLink($class_method[1], $class_method[0], $package)) return $test; } if ($test = Converter::getConstLink($expr, $class, $package)) return $test; $cl = $this->classes->getClassByPackage($class,$package); if ($cl) { $par = $cl->getParent($this); if (is_object($par)) { $package = $par->docblock->package; $class = $par->getName(); } else $class = $par; } else $class = false; } while ($class); // no links found return false; } /** * take URL $link and text $text and return a link in the format needed for the Converter * @param string URL * @param string text to display * @return string link to $link * @abstract */ function returnLink($link,$text) { } /** * take {@link abstractLink} descendant and text $eltext and return a link * in the format needed for the Converter * @param abstractLink * @param string * @return string link to $element * @abstract */ function returnSee(&$link, $eltext = false) { } /** * take {@link abstractLink} descendant and text $eltext and return a * unique ID in the format needed for the Converter * @param abstractLink * @return string unique identifier of $element * @abstract */ function getId(&$link) { } /** * Convert README/INSTALL/CHANGELOG file contents to output format * @param README|INSTALL|CHANGELOG * @param string contents of the file * @abstract */ function Convert_RIC($name, $contents) { } /** * Convert all elements to output format * * This will call ConvertXxx where Xxx is {@link ucfirst}($element->type). * It is expected that a child converter defines a handler for every * element type, even if that handler does nothing. phpDocumentor will * terminate with an error if a handler doesn't exist. * {@internal * Since 1.2.0 beta 3, this function has been moved from child converters * to the parent, because it doesn't really make sense to put it in the * child converter, and we can add error handling. * * {@source}}} * @throws {@link PDERROR_NO_CONVERT_HANDLER} * @param mixed {@link parserElement} descendant or {@link parserPackagePage} or {@link parserData} */ function Convert(&$element) { $handler = 'convert'.ucfirst($element->type); if (method_exists($this,$handler)) { $this->$handler($element); } else { addErrorDie(PDERROR_NO_CONVERTER_HANDLER,$element->type,$handler,phpDocumentor_get_class($this)); } } /**#@+ * Conversion Handlers * * All of the convert* handlers set up template variables for the Smarty * template.{@internal In addition, the {@link newSmarty()} method is * called to retrieve the global Smarty template}} */ /** * Default Tutorial Handler * * Sets up the tutorial template, and its prev/next/parent links * {@internal * Retrieves the title using {@link parserTutorial::getTitle()} and uses the * {@link parserTutorial::prev, parserTutorial::next, parserTutorial::parent} * links to set up those links.}} * @param parserTutorial */ function &convertTutorial(&$element) { $this->package = $element->package; $this->subpackage = $element->subpackage; $x = $element->Convert($this); $template = &$this->newSmarty(); $template->assign('contents',$x); $template->assign('title',$element->getTitle($this)); $template->assign('nav',$element->parent || $element->prev || $element->next); if ($element->parent) { $template->assign('up',$this->getId($element->parent)); $template->assign('uptitle',$element->parent->title); } if ($element->prev) { $template->assign('prev',$this->getId($element->prev)); $template->assign('prevtitle',$element->prev->title); } if ($element->next) { $template->assign('next',$this->getId($element->next)); $template->assign('nexttitle',$element->next->title); } return $template; } /** * Default Class Handler * * Sets up the class template. * {@internal special methods * {@link generateChildClassList(), generateFormattedClassTree()}, * {@link getFormattedConflicts, getFormattedInheritedMethods}, * and {@link getFormattedInheritedVars} are called to complete vital * template setup.}} */ function convertClass(&$element) { $this->class = $element->getName(); $this->class_data = &$this->newSmarty(); $this->class_data->assign("class_name",$element->getName()); $this->class_data->assign("vars",array()); $this->class_data->assign("methods",array()); $this->class_data->assign("consts",array()); $this->class_data->assign("is_interface", $element->isInterface()); $this->class_data->assign("implements", $this->getFormattedImplements($element)); $this->class_data->assign("package",$element->docblock->package); $this->class_data->assign("line_number",$element->getLineNumber()); $this->class_data->assign("source_location",$element->getSourceLocation($this)); $this->class_data->assign("page_link",$this->getCurrentPageLink()); $docblock = $this->prepareDocBlock($element, false); $this->class_data->assign("sdesc",$docblock['sdesc']); $this->class_data->assign("desc",$docblock['desc']); $this->class_data->assign("access", $docblock['access']); $this->class_data->assign("abstract", $docblock['abstract']); $this->class_data->assign("tags",$docblock['tags']); $this->class_data->assign("api_tags",$docblock['api_tags']); $this->class_data->assign("info_tags",$docblock['info_tags']); $this->class_data->assign("utags",$docblock['utags']); if ($this->hasSourceCode($element->getPath())) { $this->class_data->assign("class_slink",$this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true)); } else $this->class_data->assign("class_slink",false); $this->class_data->assign("children", $this->generateChildClassList($element)); $this->class_data->assign("class_tree", $this->generateFormattedClassTree($element)); $this->class_data->assign("conflicts", $this->getFormattedConflicts($element,"classes")); $inherited_methods = $this->getFormattedInheritedMethods($element); if (!empty($inherited_methods)) { $this->class_data->assign("imethods",$inherited_methods); } else { $this->class_data->assign("imethods",false); } $inherited_vars = $this->getFormattedInheritedVars($element); if (!empty($inherited_vars)) { $this->class_data->assign("ivars",$inherited_vars); } else { $this->class_data->assign("ivars",false); } $inherited_consts = $this->getFormattedInheritedConsts($element); if (!empty($inherited_consts)) { $this->class_data->assign("iconsts",$inherited_consts); } else { $this->class_data->assign("iconsts",false); } } /** * Converts method for template output * * This function must be called by a child converter with any extra * template variables needed in the parameter $addition * @param parserMethod */ function convertMethod(&$element, $additions = array()) { $fname = $element->getName(); $docblock = $this->prepareDocBlock($element); $returntype = 'void'; if ($element->isConstructor) $returntype = $element->class; if ($element->docblock->return) { $a = $element->docblock->return->Convert($this); $returntype = $element->docblock->return->converted_returnType; } $params = $param_i = array(); if (count($element->docblock->params)) foreach($element->docblock->params as $param => $val) { $a = $val->Convert($this); $params[] = $param_i[$param] = array("var" => $param,"datatype" => $val->converted_returnType,"data" => $a); } if ($element->docblock->hasaccess) $acc = $element->docblock->tags['access'][0]->value; else $acc = 'public'; if ($this->hasSourceCode($element->getPath())) $additions["slink"] = $this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true); $this->class_data->append('methods',array_merge( array('sdesc' => $docblock['sdesc'], 'desc' => $docblock['desc'], 'access' => $docblock['access'], 'abstract' => $docblock['abstract'], 'tags' => $docblock['tags'], 'api_tags' => $docblock['api_tags'], 'info_tags' => $docblock['info_tags'], 'utags' => $docblock['utags'], 'constructor' => $element->isConstructor, 'access' => $acc, 'function_name' => $fname, 'function_return' => $returntype, 'function_call' => $element->getFunctionCall(), 'ifunction_call' => $element->getIntricateFunctionCall($this, $param_i), 'descmethod' => $this->getFormattedDescMethods($element), 'method_overrides' => $this->getFormattedOverrides($element), 'line_number' => $element->getLineNumber(), 'id' => $this->getId($element), 'params' => $params), $additions)); } /** * Converts class variables for template output. * * This function must be called by a child converter with any extra * template variables needed in the parameter $addition * @param parserVar */ function convertVar(&$element, $additions = array()) { $docblock = $this->prepareDocBlock($element); $b = 'mixed'; if ($element->docblock->hasaccess) $acc = $element->docblock->tags['access'][0]->value; else $acc = 'public'; if ($element->docblock->var) { $b = $element->docblock->var->converted_returnType; } if ($this->hasSourceCode($element->getPath())) $additions["slink"] = $this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true); $this->class_data->append('vars',array_merge( array('sdesc' => $docblock['sdesc'], 'desc' => $docblock['desc'], 'abstract' => $docblock['abstract'], 'utags' => $docblock['utags'], 'tags' => $docblock['tags'], 'api_tags' => $docblock['api_tags'], 'info_tags' => $docblock['info_tags'], 'var_name' => $element->getName(), 'var_default' => $this->postProcess($element->getValue()), 'var_type' => $b, 'access' => $acc, 'line_number' => $element->getLineNumber(), 'descvar' => $this->getFormattedDescVars($element), 'var_overrides' => $this->getFormattedOverrides($element), 'id' => $this->getId($element)), $additions)); } /** * Converts class constants for template output. * * This function must be called by a child converter with any extra * template variables needed in the parameter $addition * @param parserConst */ function convertConst(&$element, $additions = array()) { $docblock = $this->prepareDocBlock($element); if ($element->docblock->hasaccess) $acc = $element->docblock->tags['access'][0]->value; else $acc = 'public'; if ($this->hasSourceCode($element->getPath())) $additions["slink"] = $this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true); $this->class_data->append('consts',array_merge( array('sdesc' => $docblock['sdesc'], 'desc' => $docblock['desc'], 'access' => $docblock['access'], 'abstract' => $docblock['abstract'], 'utags' => $docblock['utags'], 'tags' => $docblock['tags'], 'api_tags' => $docblock['api_tags'], 'info_tags' => $docblock['info_tags'], 'const_name' => $element->getName(), 'const_value' => $this->postProcess($element->getValue()), 'access' => $acc, 'line_number' => $element->getLineNumber(), 'id' => $this->getId($element)), $additions)); } /** * Default Page Handler * * {@internal In addition to setting up the smarty template with {@link newSmarty()}, * this class uses {@link getSourceLocation()} and {@link getClassesOnPage()} * to set template variables. Also used is {@link getPageName()}, to get * a Converter-specific name for the page.}} * @param parserPage */ function convertPage(&$element) { $this->page_data = &$this->newSmarty(true); $this->page = $this->getPageName($element->parent); $this->path = $element->parent->getPath(); $this->curpage = &$element->parent; $this->page_data->assign("source_location",$element->parent->getSourceLocation($this)); $this->page_data->assign("functions",array()); $this->page_data->assign("includes",array()); $this->page_data->assign("defines",array()); $this->page_data->assign("globals",array()); $this->page_data->assign("classes",$this->getClassesOnPage($element)); $this->page_data->assign("name", $element->parent->getFile()); if ($t = $element->getTutorial()) { $this->page_data->assign("tutorial",$this->returnSee($t)); } else { $this->page_data->assign("tutorial",false); } if ($element->docblock) { $docblock = $this->prepareDocBlock($element, false); $this->page_data->assign("sdesc",$docblock['sdesc']); $this->page_data->assign("desc",$docblock['desc']); $this->page_data->assign("tags",$docblock['tags']); $this->page_data->assign("api_tags",$docblock['api_tags']); $this->page_data->assign("info_tags",$docblock['info_tags']); $this->page_data->assign("utags",$docblock['utags']); } } /** * Converts global variables for template output * * This function must be called by a child converter with any extra * template variables needed in the parameter $addition * {@internal * In addition to using {@link prepareDocBlock()}, this method also * uses utility functions {@link getGlobalValue(), getFormattedConflicts()}}} * @param parserGlobal * @uses postProcess() on global_value template value, makes it displayable * @param array any additional template variables should be in this array */ function convertGlobal(&$element, $addition = array()) { $docblock = $this->prepareDocBlock($element); $value = $this->getGlobalValue($element->getValue()); if ($this->hasSourceCode($element->getPath())) $addition["slink"] = $this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true); $this->page_data->append('globals',array_merge( array('sdesc' => $docblock['sdesc'], 'desc' => $docblock['desc'], 'tags' => $docblock['tags'], 'api_tags' => $docblock['api_tags'], 'info_tags' => $docblock['info_tags'], 'utags' => $docblock['utags'], 'global_name' => $element->getName(), 'global_type' => $element->getDataType($this), 'global_value' => $this->postProcess($value), 'line_number' => $element->getLineNumber(), 'global_conflicts' => $this->getFormattedConflicts($element,"global variables"), 'id' => $this->getId($element)), $addition)); } /** * Converts defines for template output * * This function must be called by a child converter with any extra * template variables needed in the parameter $addition * {@internal * In addition to using {@link prepareDocBlock()}, this method also * uses utility functions {@link getGlobalValue(), getFormattedConflicts()}}} * @param parserDefine * @uses postProcess() on define_value template value, makes it displayable * @param array any additional template variables should be in this array */ function convertDefine(&$element, $addition = array()) { $docblock = $this->prepareDocBlock($element); if ($this->hasSourceCode($element->getPath())) $addition["slink"] = $this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true); $this->page_data->append('defines',array_merge( array('sdesc' => $docblock['sdesc'], 'desc' => $docblock['desc'], 'tags' => $docblock['tags'], 'api_tags' => $docblock['api_tags'], 'info_tags' => $docblock['info_tags'], 'utags' => $docblock['utags'], 'define_name' => $element->getName(), 'line_number' => $element->getLineNumber(), 'define_value' => $this->postProcess($element->getValue()), 'define_conflicts' => $this->getFormattedConflicts($element,"defines"), 'id' => $this->getId($element)), $addition)); } /** * Converts includes for template output * * This function must be called by a child converter with any extra * template variables needed in the parameter $addition * @see prepareDocBlock() * @param parserInclude */ function convertInclude(&$element, $addition = array()) { $docblock = $this->prepareDocBlock($element); $per = $this->getIncludeValue($element->getValue(), $element->getPath()); if ($this->hasSourceCode($element->getPath())) $addition["slink"] = $this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true); $this->page_data->append('includes',array_merge( array('sdesc' => $docblock['sdesc'], 'desc' => $docblock['desc'], 'tags' => $docblock['tags'], 'api_tags' => $docblock['api_tags'], 'info_tags' => $docblock['info_tags'], 'utags' => $docblock['utags'], 'include_name' => $element->getName(), 'line_number' => $element->getLineNumber(), 'include_value' => $per), $addition)); } /** * Converts function for template output * * This function must be called by a child converter with any extra * template variables needed in the parameter $addition * @see prepareDocBlock() * @param parserFunction */ function convertFunction(&$element, $addition = array()) { $docblock = $this->prepareDocBlock($element); $fname = $element->getName(); $params = $param_i = array(); if (count($element->docblock->params)) foreach($element->docblock->params as $param => $val) { $a = $val->Convert($this); $params[] = $param_i[$param] = array("var" => $param,"datatype" => $val->converted_returnType,"data" => $a); } $returntype = 'void'; if ($element->docblock->return) { $a = $element->docblock->return->Convert($this); $returntype = $element->docblock->return->converted_returnType; } if ($this->hasSourceCode($element->getPath())) $addition["slink"] = $this->getSourceAnchor($element->getPath(),$element->getLineNumber(),$element->getLineNumber(),true); $this->page_data->append('functions',array_merge( array('sdesc' => $docblock['sdesc'], 'desc' => $docblock['desc'], 'tags' => $docblock['tags'], 'api_tags' => $docblock['api_tags'], 'info_tags' => $docblock['info_tags'], 'utags' => $docblock['utags'], 'function_name' => $fname, 'function_return' => $returntype, 'function_conflicts' => $this->getFormattedConflicts($element,"functions"), 'ifunction_call' => $element->getIntricateFunctionCall($this, $param_i), 'function_call' => $element->getFunctionCall(), 'line_number' => $element->getLineNumber(), 'id' => $this->getId($element), 'params' => $params), $addition)); } /**#@-*/ /** * convert the element's DocBlock for output * * This function converts all tags and descriptions for output * @param mixed any descendant of {@link parserElement}, or {@link parserData} * @param array used to translate tagnames into other tags * @param boolean set to false for pages and classes, the only elements allowed to specify @package * @return array * * Format: * <pre> * array('sdesc' => DocBlock summary * 'desc' => DocBlock detailed description * 'tags' => array('keyword' => tagname, 'data' => tag description) * known tags * 'api_tags' => array('keyword' => tagname, 'data' => tag description) * known api documentation tags * 'info_tags' => array('keyword' => tagname, 'data' => tag description) * known informational tags * [ 'utags' => array('keyword' => tagname, 'data' => tag description * unknown tags ] * [ 'vartype' => type from @var/@return tag ] * [ 'var_descrip' => description from @var/@return tag ] * ) * </pre> */ function prepareDocBlock(&$element, $names = array(),$nopackage = true) { $tagses = $element->docblock->listTags(); $tags = $ret = $api_tags = $info_tags = array(); $api_tags_arr = array("abstract", "access", "deprecated", "example", "filesource", "global", "internal", "name", "return", "see", "static", "staticvar", "uses", "var"); if (!$nopackage) { $tags[] = array('keyword' => 'package','data' => $element->docblock->package); if (!empty($element->docblock->subpackage)) $tags[] = array('keyword' => 'subpackage','data' => $element->docblock->subpackage); } if ($element->docblock->var) { $a = $element->docblock->var->Convert($this); $ret['vartype'] = $element->docblock->var->converted_returnType; if (!empty($a)) { $tags[] = array('keyword' => 'var', 'data' => $a); $ret["var_descrip"] = $a; } } if ($element->docblock->return) { $a = $element->docblock->return->Convert($this); $ret['vartype'] = $element->docblock->return->converted_returnType; if (!empty($a)) { $tags[] = $api_tags[] = array('keyword' => 'return', 'data' => $a); $ret["var_descrip"] = $a; } } if ($element->docblock->funcglobals) foreach($element->docblock->funcglobals as $global => $val) { if ($a = $this->getGlobalLink($global,$element->docblock->package)) { $global = $a; } $b = Converter::getLink($val[0]); if (is_object($b) && phpDocumentor_get_class($b) == 'classlink') { $val[0] = $this->returnSee($b); } $tags[] = $api_tags[] = array('keyword' => 'global','data' => $val[0].' '.$global.': '.$val[1]->Convert($this)); } if ($element->docblock->statics) foreach($element->docblock->statics as $static => $val) { $a = $val->Convert($this); $tags[] = $api_tags[] = array('keyword' => 'staticvar','data' => $val->converted_returnType.' '.$static.': '.$a); } foreach($tagses as $tag) { if (isset($names[$tag->keyword])) $tag->keyword = $names[$tag->keyword]; if ($tag->keyword) $tags[] = array("keyword" => $tag->keyword,"data" => $tag->Convert($this)); if (in_array($tag->keyword, $api_tags_arr)) $api_tags[] = array("keyword" => $tag->keyword,"data" => $tag->Convert($this)); else $info_tags[] = array("keyword" => $tag->keyword,"data" => $tag->Convert($this)); } $utags = array(); foreach($element->docblock->unknown_tags as $keyword => $tag) { foreach($tag as $t) $utags[] = array('keyword' => $keyword, 'data' => $t->Convert($this)); } $ret['abstract'] = FALSE; $ret['access'] = 'public'; foreach($tags as $tag) { if ($tag['keyword'] == 'access') $ret['access'] = $tag['data']; if ($tag['keyword'] == 'abstract') $ret['abstract'] = TRUE; } $ret['sdesc'] = $element->docblock->getSDesc($this); $ret['desc'] = $element->docblock->getDesc($this); $ret['tags'] = $tags; $ret['api_tags'] = $api_tags; $ret['info_tags'] = $info_tags; $ret['utags'] = $utags; return $ret; } /** * gets a list of all classes declared on a procedural page represented by * $element, a {@link parserData} class * @param parserData &$element * @return array links to each classes documentation * * Format: * <pre> * array('name' => class name, * 'sdesc' => summary of the class * 'link' => link to the class's documentation) * </pre> */ function getClassesOnPage(&$element) { global $_phpDocumentor_setting; $a = $element->getClasses($this); $classes = array(); foreach($a as $package => $clas) { if (isset($_phpDocumentor_setting['packageoutput'])) { $packages = explode(',',$_phpDocumentor_setting['packageoutput']); if (!in_array($package, $packages)) continue; } for($i=0; $i<count($clas); $i++) { if ($this->parseprivate || ! ($clas[$i]->docblock && $clas[$i]->docblock->hasaccess && $clas[$i]->docblock->tags['access'][0]->value == 'private')) { $sdesc = ''; $r = array(); $sdesc = $clas[$i]->docblock->getSDesc($this); if ($clas[$i]->docblock->hasaccess) $r['access'] = $clas[$i]->docblock->tags['access'][0]->value; else $r['access'] = 'public'; if (isset ($clas[$i]->docblock->tags['abstract'])) $r['abstract'] = TRUE; else $r['abstract'] = FALSE; $r['name'] = $clas[$i]->getName(); $r['sdesc'] = $sdesc; $r['link'] = $this->getClassLink($clas[$i]->getName(),$package,$clas[$i]->getPath()); $classes[] = $r; } } } return $classes; } /** * returns an array containing the class inheritance tree from the root * object to the class. * * This method must be overridden, or phpDocumentor will halt with a fatal * error * @return string Converter-specific class tree for an individual class * @param parserClass class variable * @abstract */ function generateFormattedClassTree($class) { addErrorDie(PDERROR_CONVERTER_OVR_GFCT,phpDocumentor_get_class($this)); } /** * returns an array containing the class inheritance tree from the root * object to the class. * * This method must be overridden, or phpDocumentor will halt with a fatal * error * @return string Converter-specific class tree for an individual class * @param parserClass class variable * @abstract */ function getFormattedImplements($el) { $ret = array(); foreach ($el->getImplements() as $interface) { $ret[] = $this->returnSee($this->getLink($interface)); } return $ret; } /** * @param mixed {@link parserClass, parserFunction, parserDefine} or * {@link parserGlobal} * @param string type to display. either 'class','function','define' * or 'global variable' * @return array links to conflicting elements, or empty array * @uses parserClass::getConflicts() * @uses parserFunction::getConflicts() * @uses parserDefine::getConflicts() * @uses parserGlobal::getConflicts() */ function getFormattedConflicts(&$element,$type) { $conflicts = $element->getConflicts($this); $r = array(); if (!$conflicts) return false; foreach($conflicts as $package => $class) { $r[] = $class->getLink($this,$class->docblock->package); } if (!empty($r)) $r = array('conflicttype' => $type, 'conflicts' => $r); return $r; } /** * Get a list of methods in child classes that override this method * @return array empty array or array(array('link'=>link to method, * 'sdesc'=>short description of the method),...) * @uses parserMethod::getOverridingMethods() * @param parserMethod */ function getFormattedDescMethods(&$element) { $meths = $element->getOverridingMethods($this); $r = array(); for($i=0; $i<count($meths); $i++) { $ms = array(); $ms['link'] = $meths[$i]->getLink($this); $ms['sdesc'] = $meths[$i]->docblock->getSDesc($this); $r[] = $ms; } return $r; } /** * Get a list of vars in child classes that override this var * @return array empty array or array('link'=>link to var, * 'sdesc'=>short description of the method * @uses parserVar::getOverridingVars() * @param parserVar */ function getFormattedDescVars(&$element) { $vars = $element->getOverridingVars($this); $r = array(); for($i=0; $i<count($vars); $i++) { $vs = array(); $vs['link'] = $vars[$i]->getLink($this); $vs['sdesc'] = $vars[$i]->docblock->getSDesc($this); $r[] = $vs; } return $r; } /** * Get the method this method overrides, if any * @return array|false array('link'=>link to overridden method, * 'sdesc'=>short description * @see parserMethod::getOverrides() * @param parserMethod */ function getFormattedOverrides(&$element) { $ovr = $element->getOverrides($this); if (!$ovr) return false; $sdesc = $ovr->docblock->getSDesc($this); return array('link' => $ovr->getLink($this),'sdesc' => $sdesc); } /** * returns a list of child classes * * @param parserClass class variable * @uses parserClass::getChildClassList() */ function generateChildClassList($class) { $kids = $class->getChildClassList($this); $list = array(); if (count($kids)) { for($i=0; $i<count($kids); $i++) { $lt['link'] = $kids[$i]->getLink($this); $lt['sdesc'] = $kids[$i]->docblock->getSDesc($this); if ($kids[$i]->docblock->hasaccess) $lt['access'] = $kids[$i]->docblock->tags['access'][0]->value; else $lt['access'] = 'public'; $lt['abstract'] = isset ($kids[$i]->docblock->tags['abstract'][0]); $list[] = $lt; } } else return false; return $list; } /** * Return template-enabled list of inherited variables * * uses parserVar helper function getInheritedVars and generates a * template-enabled list using getClassLink() * @param parserVar $child class method * @see getClassLink(), parserVar::getInheritedVars() * @return array Format: * <pre> * array( * array('parent_class' => link to parent class's documentation, * 'ivars' => * array( * array('name' => inherited variable name, * 'link' => link to inherited variable's documentation, * 'default' => default value of inherited variable, * 'sdesc' => summary of inherited variable), * ...), * ...) * </pre> */ function getFormattedInheritedVars($child) { $package = $child->docblock->package; $subpackage = $child->docblock->subpackage; $ivars = $child->getInheritedVars($this); $results = array(); if (!count($ivars)) return $results; foreach($ivars as $parent => $vars) { $file = $vars['file']; $vars = $vars['vars']; $par = $this->classes->getClass($parent,$file); $package = $par->docblock->package; usort($vars,array($this,"sortVar")); $result['parent_class'] = $this->getClassLink($parent,$package); foreach($vars as $var) { $info = array(); if ($var->docblock->hasaccess) $info['access'] = $var->docblock->tags['access'][0]->value; else $info['access'] = 'public'; $info['abstract'] = isset ($var->docblock->tags['abstract'][0]); $info['name'] = $var->getName(); $info['link'] = $var->getLink($this); $info['default'] = $this->postProcess($var->getValue()); if ($var->docblock) $info['sdesc'] = $var->docblock->getSDesc($this); $result["ivars"][] = $info; } $results[] = $result; $result = array(); } return $results; } /** * Return template-enabled list of inherited methods * * uses parserMethod helper function getInheritedMethods and generates a * template-enabled list using getClassLink() * @param parserMethod $child class method * @see getClassLink(), parserMethod::getInheritedMethods() * @return array Format: * <pre> * array( * array('parent_class' => link to parent class's documentation, * 'ivars' => * array( * array('name' => inherited variable name, * 'link' => link to inherited variable's documentation, * 'function_call' => {@link parserMethod::getIntricateFunctionCall()} * returned array, * 'sdesc' => summary of inherited variable), * ...), * ...) * </pre> */ function getFormattedInheritedMethods($child) { $package = $child->docblock->package; $subpackage = $child->docblock->subpackage; $imethods = $child->getInheritedMethods($this); $results = array(); if (!count($imethods)) return $results; foreach($imethods as $parent => $methods) { $file = $methods['file']; $methods = $methods['methods']; $par = $this->classes->getClass($parent,$file); $package = $par->docblock->package; usort($methods,array($this,"sortMethod")); $result['parent_class'] = $this->getClassLink($parent,$package); foreach($methods as $method) { $info = array(); if ($method->docblock->hasaccess) $info['access'] = $method->docblock->tags['access'][0]->value; else $info['access'] = 'public'; $info['abstract'] = isset ($method->docblock->tags['abstract'][0]); if ($method->isConstructor) $info['constructor'] = 1; $info['link'] = $method->getLink($this); $info['name'] = $method->getName(); if ($method->docblock) $info['sdesc'] = $method->docblock->getSDesc($this); $params = array(); if (count($method->docblock->params)) foreach($method->docblock->params as $param => $val) { $a = $val->Convert($this); $params[$param] = array("var" => $param,"datatype" => $val->converted_returnType,"data" => $a); } $info['function_call'] = $method->getIntricateFunctionCall($this,$params); $result["imethods"][] = $info; } $results[] = $result; $result = array(); } return $results; } /** * Return template-enabled list of inherited class constants * * uses parserConst helper function getInheritedConsts and generates a * template-enabled list using getClassLink() * @param parserConst $child class constant * @see getClassLink(), parserMethod::getInheritedConsts() * @return array Format: * <pre> * array( * array('parent_class' => link to parent class's documentation, * 'ivars' => * array( * array('name' => inherited constant name, * 'link' => link to inherited constant's documentation, * 'value' => constant value, * 'sdesc' => summary of inherited constant), * ...), * ...) * </pre> */ function getFormattedInheritedConsts($child) { $package = $child->docblock->package; $subpackage = $child->docblock->subpackage; $ivars = $child->getInheritedConsts($this); $results = array(); if (!count($ivars)) return $results; foreach($ivars as $parent => $vars) { $file = $vars['file']; $vars = $vars['consts']; $par = $this->classes->getClass($parent,$file); $package = $par->docblock->package; usort($vars,array($this,"sortVar")); $result['parent_class'] = $this->getClassLink($parent,$package); foreach($vars as $var) { $info = array(); if ($var->docblock->hasaccess) $info['access'] = $var->docblock->tags['access'][0]->value; else $info['access'] = 'public'; $info['name'] = $var->getName(); $info['link'] = $var->getLink($this); $info['value'] = $this->postProcess($var->getValue()); if ($var->docblock) $info['sdesc'] = $var->docblock->getSDesc($this); $result["iconsts"][] = $info; } $results[] = $result; $result = array(); } return $results; } /** * Return a Smarty template object to operate with * * This returns a Smarty template with pre-initialized variables for use. * If the method "SmartyInit()" exists, it is called. * @return Smarty */ function &newSmarty() { $templ = new Smarty; $templ->use_sub_dirs = false; $templ->template_dir = realpath($this->smarty_dir . PATH_DELIMITER . 'templates'); $templ->compile_dir = realpath($this->smarty_dir . PATH_DELIMITER . 'templates_c'); $templ->config_dir = realpath($this->smarty_dir . PATH_DELIMITER . 'configs'); $templ->assign("date",date("r",time())); $templ->assign("maintitle",$this->title); $templ->assign("package",$this->package); $templ->assign("phpdocversion",PHPDOCUMENTOR_VER); $templ->assign("phpdocwebsite",PHPDOCUMENTOR_WEBSITE); $templ->assign("subpackage",$this->subpackage); if (method_exists($this,'SmartyInit')) return $this->SmartyInit($templ); return $templ; } /** * do all necessary output * @see Converter * @abstract */ function Output($title) { phpDocumentor_out("WARNING: Generic Converter::Output was used, no output will be generated"); } /** * Set the template directory with a different template base directory * @tutorial phpDocumentor.howto.pkg#using.command-line.templatebase * @param string template base directory * @param string template name */ function setTemplateBase($base, $dir) { // remove trailing /'s from the base path, if any $base = str_replace('\\','/',$base); while ($base{strlen($base) - 1} == '/') $base = substr($base,0,strlen($base) - 1); $this->templateName = substr($dir,0,strlen($dir) - 1); $this->templateDir = $base . "/Converters/" . $this->outputformat . "/" . $this->name . "/templates/" . $dir; if (!is_dir($this->templateDir)) { addErrorDie(PDERROR_TEMPLATEDIR_DOESNT_EXIST, $this->templateDir); } $this->smarty_dir = $this->templateDir; if (file_exists($this->templateDir . PATH_DELIMITER . 'options.ini')) { // retrieve template options, allow array creation $this->template_options = phpDocumentor_parse_ini_file($this->templateDir . PATH_DELIMITER . 'options.ini',true); } } /** * sets the template directory based on the {@link $outputformat} and {@link $name} * Also sets {@link $templateName} to the $dir parameter * @param string subdirectory */ function setTemplateDir($dir) { if ('@DATA-DIR@' != '@'.'DATA-DIR@') { $templateBase = str_replace('\\', '/', '@DATA-DIR@/PhpDocumentor/phpDocumentor'); } else { $templateBase = str_replace('\\','/',$GLOBALS['_phpDocumentor_install_dir']) . '/phpDocumentor'; } $this->setTemplateBase($templateBase, $dir); } /** * Get the absolute path to the converter's base directory * @return string */ function getConverterDir() { if (/*@donotremove*/0) { return str_replace('\\', '/', "@DATA-DIR@/PhpDocumentor/phpDocumentor/Converters/") . $this->outputformat . "/" . $this->name; } else { return str_replace('\\','/',$GLOBALS['_phpDocumentor_install_dir']) ."/phpDocumentor/Converters/" . $this->outputformat . "/" . $this->name; } } /** * Parse a global variable's default value for class initialization. * * If a global variable's default value is "new class" as in: * <code> * $globalvar = new Parser * </code> * This method will document it not as "new Parser" but instead as * "new {@link Parser}". For examples, see {@link phpdoc.inc}. * Many global variables are classes, and phpDocumentor links to their * documentation * @return string default global variable value with link to class if * it's "new Class" * @param string default value of a global variable. */ function getGlobalValue($value) { if (strpos($value,'new') === 0) { preg_match('/new([^(]*)(.*)/',$value,$newval); if (isset($newval[1])) { $a = Converter::getLink(trim($newval[1])); if (!isset($newval[2])) $newval[2] = ''; if ($a && phpDocumentor_get_class($a) == 'classlink') $value = 'new '.$this->returnSee($a).$newval[2]; } } return $value; } /** * Parse an include's file to see if it is a file documented in this project * * Although not very smart yet, this method will try to look for the * included file file.ext: * * <code> * include ("file.ext"); * </code> * * If it finds it, it will return a link to the file's documentation. As of * 1.2.0rc1, phpDocumentor is smarty enough to find these cases: * <ul> * <li>absolute path to file</li> * <li>./file.ext or ../file.ext</li> * <li>relpath/to/file.ext if relpath is a subdirectory of the base parse * directory</li> * </ul> * For examples, see {@link Setup.inc.php} includes. * Every include auto-links to the documentation for the file that is included * @return string included file with link to docs for file, if found * @param string file included by include statement. * @param string path of file that has the include statement */ function getIncludeValue($value, $ipath) { preg_match('/"([^"\']*\.[^"\']*)"/',$value,$match); if (!isset($match[1])) preg_match('/\'([^"\']*\.[^"\']*)\'/',$value,$match); if (isset($match[1])) { $fancy_per = $this->proceduralpages->pathMatchesParsedFile($match[1],$ipath); if ($fancy_per) { $link = $this->addLink($fancy_per); if (is_object($link) && phpDocumentor_get_class($link) == 'pagelink' && isset($this->all_packages[$link->package])) { $value = $this->returnSee($link,$value); } } else { $per = Converter::getLink($match[1]); if (is_object($per) && phpDocumentor_get_class($per) == 'pagelink') $value = $this->returnSee($per); } } return $value; } /** * Recursively creates all subdirectories that don't exist in the $dir path * @param string $dir */ function createParentDir($dir) { if (empty($dir)) return; $tmp = explode(SMART_PATH_DELIMITER,$dir); array_pop($tmp); $parent = implode(SMART_PATH_DELIMITER,$tmp); if ($parent != '' && !file_exists($parent)) { $test = @mkdir($parent,0775); if (!$test) { $this->createParentDir($parent); $test = @mkdir($parent,0775); phpDocumentor_out("Creating Parent Directory $parent\n"); } else { phpDocumentor_out("Creating Parent Directory $parent\n"); } } } /** * Sets the output directory for generated documentation * @param string $dir the output directory */ function setTargetDir($dir) { if (strlen($dir) > 0) { $this->targetDir = $dir; // if directory does exist create it, this should have more error checking in the future if (!file_exists($dir)) { $tmp = str_replace(array("/","\\"),SMART_PATH_DELIMITER,$dir); if (substr($tmp,-1) == SMART_PATH_DELIMITER) { $tmp = substr($tmp,0,(strlen($tmp)-1)); } $this->createParentDir($tmp); phpDocumentor_out("Creating Directory $dir\n"); mkdir($dir,0775); } else if (!is_dir($dir)) { echo "Output path: '$dir' is not a directory\n"; die(); } } else { echo "a target directory must be specified\n try phpdoc -h\n"; die(); } } /** * Writes a file to target dir * @param string * @param string * @param boolean true if the data is binary and not text */ function writeFile($file,$data,$binary = false) { if (!file_exists($this->targetDir)) { mkdir($this->targetDir,0775); } $string = ''; if ($binary) $string = 'binary file '; phpDocumentor_out(" Writing $string".$this->targetDir . PATH_DELIMITER . $file . "\n"); flush(); $write = 'w'; if ($binary) $write = 'wb'; $fp = fopen($this->targetDir . PATH_DELIMITER . $file,$write); set_file_buffer( $fp, 0 ); fwrite($fp,$data,strlen($data)); fclose($fp); } /** * Copies a file from the template directory to the target directory * thanks to Robert Hoffmann for this fix * @param string */ function copyFile($file, $subdir = '') { if (!file_exists($this->targetDir)) { mkdir($this->targetDir,0775); } copy($this->templateDir . $subdir . PATH_DELIMITER . $file, $this->targetDir . PATH_DELIMITER . $file); } /** * Return parserStringWithInlineTags::Convert() cache state * @see parserStringWithInlineTags::Convert() * @abstract */ function getState() { return true; } /** * Compare parserStringWithInlineTags::Convert() cache state to $state * @param mixed * @see parserStringWithInlineTags::Convert() * @abstract */ function checkState($state) { return true; } } /** * @access private * @see Converter::getSortedClassTreeFromClass() */ function rootcmp($a, $b) { return strnatcasecmp($a['class'],$b['class']); } /** * @access private * @global string used to make the first tutorials converted the default package tutorials */ function tutorialcmp($a, $b) { global $phpDocumentor_DefaultPackageName; if ($a == $phpDocumentor_DefaultPackageName) return -1; if ($b == $phpDocumentor_DefaultPackageName) return 1; return strnatcasecmp($a, $b); } /** * smart htmlentities, doesn't entity the allowed tags list * Since version 1.1, this function uses htmlspecialchars instead of * htmlentities, for international support * This function has been replaced by functionality in {@link ParserDescCleanup.inc} * @param string $s * @return string browser-displayable page * @deprecated As of v1.2, No longer needed, as valid tags are parsed out of the source, * and everything else is {@link Converter::postProcess()} handled */ function adv_htmlentities($s) { return; global $phpDocumentor___html,$_phpDocumentor_html_allowed; $result = htmlspecialchars($s); $entities = array_flip(get_html_translation_table(HTML_SPECIALCHARS)); $result = strtr($result,$phpDocumentor___html); $matches = array(); preg_match_all('/(<img.*>)/U',$result,$matches); for($i=0;$i<count($matches[1]);$i++) { $result = str_replace($matches[1][$i],strtr($matches[1][$i],array_flip(get_html_translation_table(HTML_SPECIALCHARS))),$result); } preg_match_all('/(<font.*>)/U',$result,$matches); for($i=0;$i<count($matches[1]);$i++) { $result = str_replace($matches[1][$i],strtr($matches[1][$i],array_flip(get_html_translation_table(HTML_SPECIALCHARS))),$result); } preg_match_all('/(<ol.*>)/U',$result,$matches); for($i=0;$i<count($matches[1]);$i++) { $result = str_replace($matches[1][$i],strtr($matches[1][$i],array_flip(get_html_translation_table(HTML_SPECIALCHARS))),$result); } preg_match_all('/(<ul.*>)/U',$result,$matches); for($i=0;$i<count($matches[1]);$i++) { $result = str_replace($matches[1][$i],strtr($matches[1][$i],array_flip(get_html_translation_table(HTML_SPECIALCHARS))),$result); } preg_match_all('/(<li.*>)/U',$result,$matches); for($i=0;$i<count($matches[1]);$i++) { $result = str_replace($matches[1][$i],strtr($matches[1][$i],array_flip(get_html_translation_table(HTML_SPECIALCHARS))),$result); } preg_match_all('/(<a .*>)/U',$result,$matches); for($i=0;$i<count($matches[1]);$i++) { $result = str_replace($matches[1][$i],strtr($matches[1][$i],array_flip(get_html_translation_table(HTML_SPECIALCHARS))),$result); } return $result; } /** * Used solely for setting up the @uses list * @package ignore * @ignore */ class __dummyConverter extends Converter { function setTemplateDir(){} function setTargetDir(){} function getPageName(&$element) { if (phpDocumentor_get_class($element) == 'parserpage') return '_'.$element->getName(); return '_'.$element->parent->getName(); } } ?>