cvs: php4 /pear/PHPDoc README index.php prepend.php /pear/PHPDoc/accessor PhpdocAccessor.php PhpdocClassAccessor.php PhpdocDocumentAccessor.php
PhpdocIndexAccessor.php PhpdocModuleAccessor.php PhpdocWarningAccessor.php /pear/PHPDoc/analyser PhpdocAnalyser.php PhpdocClassAnalyser.php
PhpdocModuleAnalyser.php /pear/PHPDoc/core Phpdoc.php PhpdocArgvHandler.php PhpdocObject.php PhpdocSetupHandler.php /pear/PHPDoc/exceptions
PhpdocError.php /pear/PHPDoc/filehandler PhpdocFileHandler.php /pear/PHPDoc/indexer PhpdocIndexer.php /pear/PHPDoc/parser PhpdocClassParser.php
PhpdocConstantParser.php PhpdocFunctionParser.php PhpdocModuleParser.php PhpdocParser.php PhpdocParserCore.php PhpdocParserRegExp.php
PhpdocParserTags.php PhpdocUseParser.php PhpdocVariableParser.php /pear/PHPDoc/redist IT.php ITX.php /pear/PHPDoc/renderer PhpdocRendererObject.php
/pear/PHPDoc/warning PhpdocWarning.php /pear/PHPDoc/xmlexporter PhpdocXMLClassExporter.php PhpdocXMLDocumentExporter.php PhpdocXMLExporter.php
PhpdocXMLIndexExporter.php PhpdocXMLModuleExporter.php PhpdocXMLWarningExporter.php /pear/PHPDoc/xmlreader PhpdocXMLReader.php
/pear/PHPDoc/xmlwriter PhpdocXMLWriter.php

From: Date: Sun, 08 Oct 2000 10:03:23 +0000
Subject: cvs: php4 /pear/PHPDoc README index.php prepend.php /pear/PHPDoc/accessor PhpdocAccessor.php PhpdocClassAccessor.php PhpdocDocumentAccessor.php
PhpdocIndexAccessor.php PhpdocModuleAccessor.php PhpdocWarningAccessor.php /pear/PHPDoc/analyser PhpdocAnalyser.php PhpdocClassAnalyser.php
PhpdocModuleAnalyser.php /pear/PHPDoc/core Phpdoc.php PhpdocArgvHandler.php PhpdocObject.php PhpdocSetupHandler.php /pear/PHPDoc/exceptions
PhpdocError.php /pear/PHPDoc/filehandler PhpdocFileHandler.php /pear/PHPDoc/indexer PhpdocIndexer.php /pear/PHPDoc/parser PhpdocClassParser.php
PhpdocConstantParser.php PhpdocFunctionParser.php PhpdocModuleParser.php PhpdocParser.php PhpdocParserCore.php PhpdocParserRegExp.php
PhpdocParserTags.php PhpdocUseParser.php PhpdocVariableParser.php /pear/PHPDoc/redist IT.php ITX.php /pear/PHPDoc/renderer PhpdocRendererObject.php
/pear/PHPDoc/warning PhpdocWarning.php /pear/PHPDoc/xmlexporter PhpdocXMLClassExporter.php PhpdocXMLDocumentExporter.php PhpdocXMLExporter.php
PhpdocXMLIndexExporter.php PhpdocXMLModuleExporter.php PhpdocXMLWarningExporter.php /pear/PHPDoc/xmlreader PhpdocXMLReader.php
/pear/PHPDoc/xmlwriter PhpdocXMLWriter.php
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-1951@lists.php.net to get a copy of this message
sbergmann Sun Oct 8 03:03:23 2000 EDT Added files: /php4/pear/PHPDoc README index.php prepend.php /php4/pear/PHPDoc/accessor PhpdocAccessor.php PhpdocClassAccessor.php PhpdocDocumentAccessor.php PhpdocIndexAccessor.php PhpdocModuleAccessor.php PhpdocWarningAccessor.php /php4/pear/PHPDoc/analyser PhpdocAnalyser.php PhpdocClassAnalyser.php PhpdocModuleAnalyser.php /php4/pear/PHPDoc/core Phpdoc.php PhpdocArgvHandler.php PhpdocObject.php PhpdocSetupHandler.php /php4/pear/PHPDoc/exceptions PhpdocError.php /php4/pear/PHPDoc/filehandler PhpdocFileHandler.php /php4/pear/PHPDoc/indexer PhpdocIndexer.php /php4/pear/PHPDoc/parser PhpdocClassParser.php PhpdocConstantParser.php PhpdocFunctionParser.php PhpdocModuleParser.php PhpdocParser.php PhpdocParserCore.php PhpdocParserRegExp.php PhpdocParserTags.php PhpdocUseParser.php PhpdocVariableParser.php /php4/pear/PHPDoc/redist IT.php ITX.php /php4/pear/PHPDoc/renderer PhpdocRendererObject.php /php4/pear/PHPDoc/warning PhpdocWarning.php /php4/pear/PHPDoc/xmlexporter PhpdocXMLClassExporter.php PhpdocXMLDocumentExporter.php PhpdocXMLExporter.php PhpdocXMLIndexExporter.php PhpdocXMLModuleExporter.php PhpdocXMLWarningExporter.php /php4/pear/PHPDoc/xmlreader PhpdocXMLReader.php /php4/pear/PHPDoc/xmlwriter PhpdocXMLWriter.php Log: Initial commit for Ulf Wendel's PHPDoc. I hope I haven't forgot a directory or file along the way... Index: php4/pear/PHPDoc/index.php +++ php4/pear/PHPDoc/index.php <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>PHPDOC Version 1.0beta</title> </head> <body> <table width="560"> <tr> <td align="left" valign="top"> <font face="Arial, Helvetica" size="2"> <h1>Welcome to PHPDoc!</h1> PHPDoc currently requires a late PHP4 version (4.0.3dev) to work. Some earlier version like to crash with memory trouble. <p> PHPDoc was programmed without using PHP4 features that means it will run with version prior to 4.0.3dev on fine day. But again: my PHP3.0.15 crashed when it started to render the HTML documents :(. <p> I'll try to provide a solution as soon as possible. </font> </td> <tr> <td align="left" valign="top"><hr></td> </tr> <tr> <td align="left" valign="top"> <pre> <?php $start = time(); // WARNING: long runtimes! Make modifications // to the php[3].ini if neccessary. A P3-500 // needs slightly more than 30 seconds to // document phpdoc itself. // Directory with include files define("PHPDOC_INCLUDE_DIR", "c:/www/apache/doc/"); // Important: set this to the Linebreak sign of your system! define("LINEBREAK", "\r\n"); // main PHPDoc Include File include("./prepend.php"); $doc = new Phpdoc; // Sets the name of your application. // The name of the application gets used in many default templates. $doc->setApplication("PEAR Repository"); // directory where your source files reside: $doc->setSourceDirectory("c:/www/apache/form/"); // save the generated docs here: $doc->setTarget("c:/www/apache/doc/apidoc/"); // use these templates: $doc->setTemplateDirectory("c:/www/apache/doc/renderer/html/templates/"); // source files have one of these suffixes: $doc->setSourceFileSuffix( array ("php", "inc") ); // parse and generate the xml files $doc->parse(); // turn xml in to html using templates $doc->render(); printf("%d seconds needed\n\n.", time() - $start); ?> </pre> </td> </tr> <tr> <td align="left" valign="top"><hr></td> </tr> <tr> <td align="left" valign="top"> <font face="Arial, Helvetica" size="2"> <h2>Finished!</h2> PHPDoc has finished his work. The generated XML and HTML files can be found in the directory specified with setTarget() in the above code. This is per default "installationdir/apidoc/". Within this directory is another directory named "keep/". It contains a stylesheet file and a frameset you can use to browse the HTML files. <p> Don't be disappointed if PHPDoc makes documentation mistakes. Always remember it's currently only grabbing not parsing. If PHPDoc will develop as a standard this will change as soon as possible. Please be patient and wait until "... later this year." ;-). <p> Have fun! <p> <a href="mailto:ulf.wendel@phpdoc.de">Ulf</a> <p> ... and what about the warning? I use xml_parse_into_struct() as documented, but PHP4 likes to throw a warning, ignore it. </font> </td> </tr> </table> </body> </html> Index: php4/pear/PHPDoc/prepend.php +++ php4/pear/PHPDoc/prepend.php <?php require( PHPDOC_INCLUDE_DIR . "exceptions/PhpdocError.php" ); // Phpdoc Core require( PHPDOC_INCLUDE_DIR . "core/PhpdocObject.php" ); require( PHPDOC_INCLUDE_DIR . "core/PhpdocArgvHandler.php" ); require( PHPDOC_INCLUDE_DIR . "core/PhpdocSetupHandler.php" ); require( PHPDOC_INCLUDE_DIR . "core/Phpdoc.php" ); // Phpdoc Warning container require( PHPDOC_INCLUDE_DIR . "warning/PhpdocWarning.php" ); // Phpdoc File Handler require( PHPDOC_INCLUDE_DIR . "filehandler/PhpdocFileHandler.php" ); // Phpdoc Parser require( PHPDOC_INCLUDE_DIR . "parser/PhpdocParserRegExp.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocParserTags.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocParserCore.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocUseParser.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocConstantParser.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocModuleParser.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocVariableParser.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocFunctionParser.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocClassParser.php" ); require( PHPDOC_INCLUDE_DIR . "parser/PhpdocParser.php" ); // Phpdoc Analyser require( PHPDOC_INCLUDE_DIR . "analyser/PhpdocAnalyser.php" ); require( PHPDOC_INCLUDE_DIR . "analyser/PhpdocClassAnalyser.php" ); require( PHPDOC_INCLUDE_DIR . "analyser/PhpdocModuleAnalyser.php" ); // Phpdoc Indexer require( PHPDOC_INCLUDE_DIR . "indexer/PhpdocIndexer.php" ); // Phpdoc XML Writer require( PHPDOC_INCLUDE_DIR . "xmlwriter/PhpdocXMLWriter.php" ); // Phpdoc XML Exporter require( PHPDOC_INCLUDE_DIR . "xmlexporter/PhpdocXMLExporter.php" ); require( PHPDOC_INCLUDE_DIR . "xmlexporter/PhpdocXMLIndexExporter.php" ); require( PHPDOC_INCLUDE_DIR . "xmlexporter/PhpdocXMLWarningExporter.php" ); require( PHPDOC_INCLUDE_DIR . "xmlexporter/PhpdocXMLDocumentExporter.php"); require( PHPDOC_INCLUDE_DIR . "xmlexporter/PhpdocXMLModuleExporter.php" ); require( PHPDOC_INCLUDE_DIR . "xmlexporter/PhpdocXMLClassExporter.php" ); // Redistributed IT[X] Templates from the PHPLib require( PHPDOC_INCLUDE_DIR . "redist/IT.php" ); require( PHPDOC_INCLUDE_DIR . "redist/ITX.php" ); // XML Reader require( PHPDOC_INCLUDE_DIR . "xmlreader/PhpdocXMLReader.php" ); // API to access XML data require( PHPDOC_INCLUDE_DIR . "accessor/PhpdocAccessor.php" ); require( PHPDOC_INCLUDE_DIR . "accessor/PhpdocIndexAccessor.php" ); require( PHPDOC_INCLUDE_DIR . "accessor/PhpdocWarningAccessor.php" ); require( PHPDOC_INCLUDE_DIR . "accessor/PhpdocDocumentAccessor.php" ); require( PHPDOC_INCLUDE_DIR . "accessor/PhpdocClassAccessor.php" ); require( PHPDOC_INCLUDE_DIR . "accessor/PhpdocModuleAccessor.php" ); // Phpdoc Renderer require( PHPDOC_INCLUDE_DIR . "renderer/PhpdocRendererObject.php" ); require( PHPDOC_INCLUDE_DIR . "renderer/html/PhpdocHTMLRenderer.php" ); require( PHPDOC_INCLUDE_DIR . "renderer/html/PhpdocHTMLIndexRenderer.php" ); require( PHPDOC_INCLUDE_DIR . "renderer/html/PhpdocHTMLWarningRenderer.php" ); require( PHPDOC_INCLUDE_DIR . "renderer/html/PhpdocHTMLDocumentRenderer.php" ); require( PHPDOC_INCLUDE_DIR . "renderer/html/PhpdocHTMLModuleRenderer.php" ); require( PHPDOC_INCLUDE_DIR . "renderer/html/PhpdocHTMLClassRenderer.php" ); require( PHPDOC_INCLUDE_DIR . "renderer/html/PhpdocHTMLRendererManager.php" ); ?> Index: php4/pear/PHPDoc/accessor/PhpdocAccessor.php +++ php4/pear/PHPDoc/accessor/PhpdocAccessor.php <?php /** * Provides an API to access PHPDoc XML files. * * It's up to you eigther to use this class to access * the phpdoc xml files or to write your own parser. */ class PhpdocAccessor extends PhpdocObject { /** * Instance of PhpdocXMLReader * @var object PhpdocXMLReader $xmlreader */ var $xmlreader; /** * Result of the PhpdocXMLReader * @var array $xml */ var $xml = array(); /** * Free xml resources on calling a getXY() function? * * One of the design goals was to minimize the memory consumption of PHPdoc. * So PHPdoc tries to save data as soon as possible to the disk, reuse objects * and free resources of an object when they are no longer needed. The default * value of true will cause the object to free the memory used by the * xml data as soon as possible. * * @var boolean */ var $freeOnGet = true; /** * Reformatted PhpdocXMLReader result array * @var array */ var $data = array(); /** * Loads the specified xml file. * * @param string Name of the xml file * @return boolean False if the given xml file was not * found or is empty otherwise true. * @access public * @see init() */ function loadXMLFile($filename) { $this->xmlreader = new PhpdocXMLReader; $this->xml = $this->xmlreader->parse($filename); $this->xml = $this->xml["phpdoc"]; $ok = (!is_array($this->xml) || 0==count($this->xml)) ? false : true; $this->init(); return $ok; } // end func loadXMLFile /** * Reformats the xml result array from the PhpdocXMLReader. * * Every derived class must override this function to call the functions * it needs to reorganize the data from the PhpdocXMLReader in a * way that it needs. * * @abstract * @see $xml, $data */ function init() { } // end func init } // end class PhpdocAccessor ?> Index: php4/pear/PHPDoc/accessor/PhpdocClassAccessor.php +++ php4/pear/PHPDoc/accessor/PhpdocClassAccessor.php <?php /** * Provides functions to access phpdoc xml documents that contain classes. * * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version 1.0 * @package PHPDoc */ class PhpdocClassAccessor extends PhpdocDocumentAccessor { var $xmlkey = "class"; /** * Array of inherited elements * @var array $inherited */ var $inherited = array(); /** * Returns an array with the data of a class (no functions etc, just the class docs). * @return array $class * @access public */ function getClassdata() { $class = $this->xml["class"]; unset($class["variable"]); unset($class["function"]); unset($class["uses"]); unset($class["constant"]); unset($class["inherited"]); unset($class["overriden"]); unset($class["path"]); return $class; } // end func getClassdata /** * Returns an array of inherited functions. * @return array * @access public * @see getInheritedVariables(), getInheritedUses(), getInheritedConstants() */ function getInheritedFunctions() { return $this->inherited["functions"]; } // end func getInheritedFunctions /** * Returns an array of inherited variables. * @return array * @access public * @see getInheritedFunctions(), getInheritedUses(), getInheritedConstants() */ function getInheritedVariables() { return $this->inherited["variables"]; } // end func getInheritedVariables /** * Returns an array of inherited included files. * @return array * @access public * @see getInheritedFunctions(), getInheritedUses(), getInheritedConstants() */ function getInheritedUses() { return $this->inherited["uses"]; } // end func getInheritedUses() /** * Returns an array of inherited constants. * @return array * @access public * @see getInheritedFunctions(), getInheritedVariables(), getInheritedUses() */ function getInheritedConstants() { return $this->inherited["constants"]; } // end func getInheritedConstants /** * Returns an array with the "path" of a class. * @return array $path * @access public * @see getSubclasses() */ function getClasstree() { if (isset($this->xml["class"]["path"])) return $this->convertPath($this->xml["class"]["path"]); else return array(); } // end func getClasstree /** * Returns an array with all subclasses of a class. * @return array * @access public * @see getClasstree() */ function getSubclasses() { return $this->data["subclasses"]; } // end func getSubclasses /** * Converts a xml path array to a path that can be passed to the user. * * The path is an array like path[0..n] = classname where path[0] is the * directs parent (extends path[0]) and path[n] is the baseclass. * * @param array $xmlpath * @return array $path */ function convertPath($xmlpath) { $path = array(); if (!isset($xmlpath["parent"][0])) { $path[0] = $xmlpath["parent"]["value"]; } else { reset($xmlpath["parent"]); while (list($k, $parent)=each($xmlpath["parent"])) $path[] = $parent["value"]; } return $path; } // end func convertPath /** * Builds a list of inherited elements. * @see $inherited */ function buildInheritedlist() { $this->inherited = array( "functions" => array(), "variables" => array(), "constants" => array(), "uses" => array() ); if (isset($this->xml["class"]["inherited"])) { if (isset($this->xml["class"]["inherited"][0])) { reset($this->xml["class"]["inherited"]); while (list($k, $inherited)=each($this->xml["class"]["inherited"])) { $type = $inherited["type"]; $src = $inherited["src"]; if (isset($inherited["element"][0])) { reset($inherited["element"]); while (list($k2, $element)=each($inherited["element"])) $this->inherited[$type][$src][] = $element["value"]; } else { $this->inherited[$type][$src][] = $inherited["element"]["value"]; } } } else { $inherited = $this->xml["class"]["inherited"]; $type = $inherited["type"]; $src = $inherited["src"]; if (isset($inherited["element"][0])) { reset($inherited["element"]); while (list($k, $element)=each($inherited["element"])) $this->inherited[$type][$src][] = $element["value"]; } else { $this->inherited[$type][$src][] = $inherited["element"]["value"]; } } unset($this->xml["class"]["inherited"]); } } // end func buildInheritedlist /** * Builds a list of subclasses */ function buildSubclasslist() { $this->data["subclasses"] = array(); if (isset($this->xml["class"]["subclasses"])) { if (isset($this->xml["class"]["subclasses"]["subclass"][0])) { reset($this->xml["class"]["subclasses"]["subclass"]); while (list($k, $subclass)=each($this->xml["class"]["subclasses"]["subclass"])) $this->data["subclasses"][] = $subclass["value"]; } else { $this->data["subclasses"][] = $this->xml["class"]["subclasses"]["subclass"]["value"]; } } } // end func buildSubclasslist function init() { #$this->introspection("xml", $this->xml); $this->buildInheritedlist(); $this->buildSubclasslist(); list($this->data["functions"], $this->data["functionsaccess"]) = $this->getElementlist("function"); list($this->data["variables"], $this->data["variablesaccess"]) = $this->getElementlist("variable"); list($this->data["constants"], $this->data["constantsaccess"]) = $this->getElementlist("constant"); $this->buildUseslist(); #$this->introspection("data", $this->data); } // end func Init } // end class PhpdocClassAccessor ?> Index: php4/pear/PHPDoc/accessor/PhpdocDocumentAccessor.php +++ php4/pear/PHPDoc/accessor/PhpdocDocumentAccessor.php <?php /** * Base of the class and module accessor. */ class PhpdocDocumentAccessor extends PhpdocAccessor { /** * Kind of top-level container in the xml document. * * Must be set by all derived classes. * * @var string */ var $xmlkey = ""; /** * Returns an array with all functions. * @return array $functions * @access public * @see getFunctionsByAccess() */ function getFunctions() { return $this->getElements("functions", "functionsaccess"); } // end func getFunctions /** * Returns an array with all functions with a certain access (public, private) attribute. * @param string Requested access attribute. * @return array $functions * @access public * @see getFunctions() */ function getFunctionsByAccess($access) { return $this->getElementsByAccess($access, "functions", "functionsaccess"); } // end func getFunctionByAccess /** * Returns an array with all variables. * @return array $variables * @access public * @see getVariablesByAccess() */ function getVariables() { return $this->getElements("variables", "variablesaccess"); } // end func getVariables /** * Returns an array with all variables with a certain access (public, private) attribute. * @param string Requested access attribute. * @return array $variables * @access public * @see getVariables() */ function getVariablesByAccess($access) { return $this->getElementsByAccess($access, "variables", "variablesaccess"); } // end func getVariablesByAccess /** * Returns an array of all constants. * @return array $constants * @access public * @see getConstantsByAccess() */ function getConstants() { return $this->getElements("constants", "constantsaccess"); } // end func getConstants /** * Returns an array of all constants with a certain access (public, private) attribute. * @param string Requested access attribute. * @return array $constants * @see getConstants() * @access public */ function getConstantsByAccess($access) { return $this->getElementsByAccess($access, "constants", "constantsaccess"); } // end func getConstantsByAccess /** * Returns an array of all included files. * @return array $uses * @see getUsesByType() * @access public */ function getUses() { return $this->getElements("uses", "usestype"); } // end func getUses /** * Returns an array of all included files with a certain type (include, require...) attribute. * @param string Requested type: include, include_once, require, require_once * @return array $uses * @access public */ function getUsesByType($type) { $data = array(); if (!isset($this->data["usestype"][$type])) return $data; reset($this->data["usestype"][$type]); while (list($k, $file)=each($this->data["usestype"][$type])) { $data[$file] = $this->data["uses"][$file]; if ($this->freeOnGet) unset($this->data["uses"][$file]); } if ($this->freeOnGet) unset($this->data["usestype"][$type]); return $data; } // end func getUsesByType /** * Returns elements from the internal $data array. * * The object uses this function to extract functions, variables, uses and * constants from an internal array. Note that this is not a public function, * future version might access internal data structures different. * * @param string Name of the element you need: functions, variables,... * @param string Name of internal element access table * @see $data */ function getElements($element, $elementaccess) { if ($this->freeOnGet) { $data = $this->data[$element]; unset($this->data[$element]); unset($this->data[$elementaccess]); return $data; } else { $this->data[$element]; } } // end func getElements /** * Returns elements with a certain access type from the internal data. * @param string Accesstype * @param string element name * @param string access type * @brother getElements() */ function getElementsByAccess($access, $element, $elementaccess) { $data = array(); if (!isset($this->data[$elementaccess][$access])) return $data; reset($this->data[$elementaccess][$access]); while (list($k, $name)=each($this->data[$elementaccess][$access])) { $data[$name] = $this->data[$element][$name]; if ($this->freeOnGet) unset($this->data[$element][$name]); } if ($this->freeOnGet) unset($this->data[$elementaccess][$access]); return $data; } // end func getElementsByAccess /** * Adds a list of @included files to the internal data array. */ function buildUseslist() { $this->data["uses"] = array(); $this->data["usestype"] = array(); if (isset($this->xml[$this->xmlkey]["uses"])) { if (isset($this->xml[$this->xmlkey]["uses"][0])) { reset($this->xml[$this->xmlkey]["uses"]); while (list($k, $data)=each($this->xml[$this->xmlkey]["uses"])) { $this->data["uses"][$data["file"]] = $data; $this->data["usestype"][$data["type"]][] = $data["file"]; } } else { $data = $this->xml[$this->xmlkey]["uses"]; $this->data["uses"][$data["file"]] = $data; $this->data["usestype"][$data["type"]][] = $data["file"]; } unset($this->xml[$this->xmlkey]["uses"]); } } // end func buildUseslist /** * Adds a list of a certain element to the internal data array. * @param string name of the element to add: function, variable, constant. */ function getElementlist($element) { $elements = array(); $elementaccess = array(); if (isset($this->xml[$this->xmlkey][$element])) { if (isset($this->xml[$this->xmlkey][$element][0])) { reset($this->xml[$this->xmlkey][$element]); while (list($k, $data)=each($this->xml[$this->xmlkey][$element])) { $elements[$data["name"]] = $data; $elementaccess[$data["access"]][] = $data["name"]; } } else { $data = $this->xml[$this->xmlkey][$element]; $elements[$data["name"]] = $data; $elementaccess[$data["access"]][] = $data["name"]; } unset($this->xml[$this->xmlkey][$element]); } return array($elements, $elementaccess); } // end func getElementlist } // end class PhpdocDocumentAccessor ?> Index: php4/pear/PHPDoc/accessor/PhpdocIndexAccessor.php +++ php4/pear/PHPDoc/accessor/PhpdocIndexAccessor.php <?php /** * Provides a API to access Index xml documents. */ class PhpdocIndexAccessor extends PhpdocAccessor { /** * Ordered list of all chapternames. * @var array */ var $chapternames = array(); /** * Ordered list of all chapters. * @var array */ var $chapters = array(); /** * List of all packages. * @var array */ var $packages = array(); /** * Data of a classtree * @var array */ var $classtree = array(); /** * Data of a modulegroup * @var array */ var $modulegroup = array(); /** * Some container withing the packagelist. * @var array * @see buildPackagelist() */ var $packageFields = array("class", "module"); /** * Flag indicating that certain internal datafield have been filled. * @var array */ var $flagBuild = array( "chapter" => false, "package" => false ); /** * Returns a modulegroup * * @access public */ function getModulegroup() { $this->buildModulegroup(); if ($this->freeOnGet) { $data = $this->modulegroup; $this->modulegroup = array(); return $data; } else { return $this->modulegroup; } } // end func getModulegroup /** * Returns a classtree. * * @return array * @access public */ function getClasstree() { $this->buildClasstree(); if ($this->freeOnGet) { $data = $this->classtree; $this->classtree = array(); return $data; } else { return $this->classtree; } } // end func getClasstree /** * Returns an ordered list of all chapternames. * * @return array * @access public * @see getChapters() */ function getChapternames() { $this->buildChapterlist(); if ($this->freeOnGet) { $data = $this->chapternames; $this->chapternames = array(); return $data; } else { return $this->chapternames; } } // end func getChapternames /** * Returns an ordered list of all chapters. * * @return array * @access public * @see getChapternames() */ function getChapters() { $this->buildChapterlist(); if ($this->freeOnGet) { $data = $this->chapters; $this->chapters = array(); return $data; } else { return $this->chapters; } } // end func getChapters /** * Returns a list of all packages * * @return array * @access public */ function getPackagelist() { $this->buildPackagelist(); if ($this->freeOnGet) { $data = $this->packages; $this->packages = array(); return $data; } else { return $this->packages; } } // end func getPackagelist /** * Builds the internal packagelist. */ function buildPackagelist() { if ($this->flagBuild["package"]) return; $data = $this->xml["packagelist"]; $this->xml = array(); $this->flagBuild["package"] = true; $this->packages = array(); if (!isset($data["package"][0])) $data["package"] = array($data["package"]); reset($data["package"]); while (list($k, $package)=each($data["package"])) { $packagename = $package["name"]; reset($this->packageFields); while (list($k, $field)=each($this->packageFields)) { if (isset($package[$field][0])) { reset($package[$field]); while (list($k, $element)=each($package[$field])) $this->packages[$packagename][$field][] = $element["name"]; } else if (isset($package[$field])) { $this->packages[$packagename][$field][] = $package[$field]["name"]; } } } } // end func buildPackagelist /** * Builds the internal chapterlists. */ function buildChapterlist() { if ($this->flagBuild["chapter"]) return; $data = $this->xml["index"]; $this->xml = array(); $this->flagBuild["chapter"] = true; $this->chapternames = array(); $this->chapters = array(); if (isset($data["chapter"][0])) { $chapterlist = array(); reset($data["chapter"]); while (list($k, $chapter)=each($data["chapter"])) $chapterlist[strtoupper($chapter["char"])][$chapter["char"]] = $k; ksort($chapterlist, SORT_STRING); reset($chapterlist); while (list($k, $chapters)=each($chapterlist)) { reset($chapters); while (list($chapter, $index)=each($chapters)) { $this->chapternames[] = $chapter; $this->chapters[$chapter] = $data["chapter"][$index]; } } } else { $this->chapternames[] = $data["chapter"]["char"]; $this->chapters[$data["chapter"]["char"]] = $data["chapter"]["char"]; } } // end func buildChapterlist /** * Extracts the modulegroup data of the xml file. * * @see getModulegroup */ function buildModulegroup() { if ($this->flagBuild["modulegroup"]) return; $this->flagBuild["modulegroup"] = true; $data = $this->xml["modulegroup"]; $this->xml = ""; $this->modulegroup = array( "group" => $data["name"], "modules" => array() ); if (!isset($data["module"][0])) $data["module"] = array( $data["module"] ); reset($data["module"]); while (list($k, $module)=each($data["module"])) $this->modulegroup["modules"][] = $module["name"]; } // end func buildModulegroup /** * Extracts the classtree data of the xml file. * * @see getClasstree() */ function buildClasstree() { if ($this->flagBuild["classtree"]) return; $this->flagBuild["classtree"] = true; $data = $this->xml["classtree"]; $this->xml = ""; $this->classtree = array( "baseclass" => $data["baseclass"], "classes" => array() ); if (!isset($data["class"][0])) $data["class"] = array( $data["class"] ); reset($data["class"]); while (list($k, $class)=each($data["class"])) { if (!isset($class["subclass"])) { $this->classtree["classes"][$class["name"]] = array(); } else { if (!isset($class["subclass"][0])) { $this->classtree["classes"][$class["name"]][] = $class["subclass"]["value"]; } else { reset($class["subclass"]); while (list($k, $subclass)=each($class["subclass"])) $this->classtree["classes"][$class["name"]][] = $subclass["value"]; } } } } // end func buildClasstree /** * Resets the build flags. * @see $flagBuild */ function init() { reset($this->flagBuild); while (list($k, $v)=each($this->flagBuild)) $this->flagBuild[$k] = false; } // end func init } // end class PhpdocIndexAccessor ?> Index: php4/pear/PHPDoc/accessor/PhpdocModuleAccessor.php +++ php4/pear/PHPDoc/accessor/PhpdocModuleAccessor.php <?php /** * Provides functions to access phpdoc xml documents that contain modules. */ class PhpdocModuleAccessor extends PhpdocDocumentAccessor { var $xmlkey = "module"; /** * Returns an array with the data of a module (no functions etc, just the module docs). * @return array $class * @access public */ function getModuledata() { $module = $this->xml["module"]; unset($module["function"]); unset($module["uses"]); unset($module["constant"]); return $module; } // end func getModuledata function init() { list($this->data["functions"], $this->data["functionsaccess"]) = $this->getElementlist("function"); list($this->data["variables"], $this->data["variablesaccess"]) = $this->getElementlist("variable"); list($this->data["constants"], $this->data["constantsaccess"]) = $this->getElementlist("constant"); $this->buildUseslist(); } // end func Init } // end class PhpdocModuleAccessor ?> Index: php4/pear/PHPDoc/accessor/PhpdocWarningAccessor.php +++ php4/pear/PHPDoc/accessor/PhpdocWarningAccessor.php <?php /** * Extracts the warnings from PHPDoc warnings_* files. * */ class PhpdocWarningAccessor extends PhpdocAccessor { /** * If set to true all get_xy() functions will free their resources. * @var boolean * @access public */ var $freeOnGet = true; /** * Array of warnings. * @var array */ var $warnings = array(); /** * Flag used to detect if get_xy() was called.s * @var boolean */ var $flag_build = false; /** * Returns a hash of warnings in of the given XML file. * * @param string XML file * @return array * @access public * @see $freeOnGet */ function getWarnings($xmlfile) { $this->buildWarnings($xmlfile); if ($this->freeOnGet) { $data = $this->warnings; $this->warnings = array(); return $data; } else { return $this->warnings; } } // end func getWarnings /** * Build the internal list of warnings. * * @param string XML file to load */ function buildWarnings($xmlfile) { if ($this->flag_build) return; $this->flag_build = true; $this->warnings = array(); $this->loadXMLFile($xmlfile); if(!isset($this->xml["warnings"][0])) $this->xml["warnings"] = array( $this->xml["warnings"] ); reset($this->xml["warnings"]); while (list($k, $warnings)=each($this->xml["warnings"])) { $file = $warnings["file"]; if (!isset($warnings["warning"][0])) $warnings["warning"] = array($warnings["warning"]); $this->warnings[$file] = $warnings["warning"]; } $this->xml = ""; } // end func buildWarnings function init() { $this->flag_build = false; } // end func init } // end class PhpdocWarningAccess ?> Index: php4/pear/PHPDoc/analyser/PhpdocAnalyser.php +++ php4/pear/PHPDoc/analyser/PhpdocAnalyser.php <?php /** * Analyses parsing data. * * Analyse means: * - update @brother/@sister * - update @access/@return * - inherit elements * - inherit information * */ class PhpdocAnalyser extends PhpdocObject { /** * Flag indicating that getModule/getClass was called. * @var boolean */ var $flag_get = false; /** * Starts the analysing of the raw parsing data. * * @access public * @abstract */ function analyse() { } // end func analyse /** * Handles @brother and @sister. * @abstract * @see updateBrotherSister */ function updateBrothersSisters() { } // end func updateBrothersSisters /** * Updates certain elements that use @brother and @sister. * * @return boolean $ok */ function updateBrotherSisterElements() { return false; } // end func updateBrotherSisterElements /** * Updates the @access and @return tag values. * * @see updateAccessReturnElements(), updateAccessElements() * @abstract */ function updateAccessReturn() { } // end func updateAccessReturn /** * Updates @access and @return for certain elements. * * This function should only be used to update functions. * Functions that have the same name as the class (constructors) * get @return void and @access public. Functions without * @access get @access public and functions without @return get * @return void. * * @return boolean $ok * @see updateAccessReturn() * @abstract */ function updateAccessReturnElements() { return false; } // end func updateAccessReturnElements /** * Updates @access tags. * * @see updateAccessReturnElements() * @abstract */ function updateAccessElements() { } // end func updateAccessElements /** * Compares the @param tags with the function head found. * @abstract */ function checkFunctionArgs() { } // end func checkFunctionArgs /** * Looks for undocumented elements and adds a warning if neccessary. * @abstract */ function findUndocumented() { } // end func findUndocumented /** * Compares the argument list generated from the function head with the @param tags found. * * PHPDoc is able to recognize these documentation mistakes: * - too few or too many @param tags * - name does not match or is missing * - type does not match or is missing * - trouble with inherited elements * * @param array Function arguments found by the parser * @param array Paramarray * @param string Functionname * @param string Filename * @param boolean Param tags inherited? * @return array $params Param array */ function checkArgDocs($args, $params, $elname, $elfile, $inherited=false) { // "args" contains the informations the parser found in the function head. // "param" contains the information from the @param tags. $num_args = count($args); $num_params = count($params); // no args? return... if (0==$num_args && 0==$num_params) { return array(); } // no args but @param used if (0==$num_args && $num_params>0) { if (!$inherited) { $msg = "Function head shows no parameters, remove all @param tags."; $this->warn->addDocWarning($elfile, "function", $elname, $msg, "mismatch"); } else { if ("void"!=$params[0]["type"]) { $msg = "The function inherited some parameter documentation from it's parentclass but PHPDoc could not find arguments in the function head. Add @param void to the doc comment to avoid confusion."; $this->warn->addDocWarning($elfile, "function", $elname, $msg, "mismatch"); } } return array(); } // compare the informations from the parser with the @param tags reset($args); while (list($k, $arg)=each($args)) { if (isset($params[$k])) { if ($arg["optional"]) $params[$k]["default"] = $arg["default"]; if (!$inherited) { if (""!=$arg["type"] && ""!=$params[$k]["type"] && strtolower($arg["type"])!=strtolower($params[$k]["type"])) { $type = $arg["type"]; $msg = sprintf("%dth parameter type '%s' does match the the documented type '%s', possible error consider an update to '@param %s %s %s' or '@param %s %s', the variable name is optional.", $k+1, $arg["name"], $params[$k]["type"], $type, $arg["name"], (isset($params[$k]["desc"])) ? $params[$k]["desc"] : "(description)", $type, (isset($params[$k]["desc"])) ? $params[$k]["desc"] : "(description)" ); $this->warn->addDocWarning($elfile, "function", $elname, $msg, "mismatch"); } else if (""!=$params[$k]["type"]) { $type = $params[$k]["type"]; } else { $msg = sprintf('Type missing for the %dth parameter, "mixed" assumed.', $k); $this->warn->addDocWarning($elfile, "function", $elname, $msg, "missing"); $type = "mixed"; } $params[$k]["type"] = $type; } else { if (""!=$params[$k]["type"] && strtolower($arg["type"])!=strtolower($params[$k]["type"])) { $type = (""!=$args["type"]) ? $arg["type"] : $params[$k]["type"]; $msg = sprintf("Possible documentation error due to inherited information. The type of the %dth parameter '%s' does not match the documented type '%s'. Override the inherited documentation if neccessary.", $k, $arg["type"], $params[$k]["type"] ); $this->warn->addDocWarning($elfile, "function", $elname, $msg, "mismatch"); } else if (""!=$params[$k]["type"]) { $type = $params[$k]["type"]; } else { $type = "mixed"; $msg = sprintf('Type missing for the %d parameter, "mixed" assumed. Override the inherited documentation if neccessary.', $k); $this->warn->addDocWarning($elfile, "function", $elname, $msg, "mismatch"); } $params[$k]["type"] = $type; } if (""!=$params[$k]["name"] && $arg["name"]!=$params[$k]["name"]) { $msg = sprintf("%dth parameter '%s' does not match the documented name '%s', update the tag to '@param %s %s %s' or '@param %s %s', the variable name is optional.", $k+1, $arg["name"], $params[$k]["name"], $type, $arg["name"], (isset($params[$k]["desc"])) ? $params[$k]["desc"] : "(description)", $type, (isset($params[$k]["desc"])) ? $params[$k]["desc"] : "(description)" ); $this->warn->addDocWarning($elfile, "function", $elname, $msg, "mismatch"); $params[$k]["name"] = $arg["name"]; } else if (""==$params[$k]["name"]) { $params[$k]["name"] = $arg["name"]; } } else { $msg = sprintf("%dth parameter '%s' is not documented add '@param %s [description]' to the end of the @param[eter] list.", $k+1, $arg["name"], (""==$arg["type"]) ? "(object objectname|type)" : $arg["type"] ); $params[$k]["name"] = $arg["name"]; $params[$k]["undoc"] = true; if (""!=$arg["type"]) $params[$k]["type"] = $arg["type"]; $this->warn->addDocWarning($elfile, "function", $elname, $msg, "missing"); } } // more @params specified than variables where found in the function head, delete them if ($num_params>$num_args) { $msg = "The parser found '$num_args' parameter but '$num_params' @param[eter] tags. You should update the @param[eter] list."; $this->warn->addDocWarning($elfile, "function", $elname, $msg, "mismatch"); for ($i=$k+1; $i<$num_params; $i++) unset($params[$i]); } return $params; } // end func checkArgDocs } // end func PhpdocAnalyser ?> Index: php4/pear/PHPDoc/analyser/PhpdocClassAnalyser.php +++ php4/pear/PHPDoc/analyser/PhpdocClassAnalyser.php <?php /** * Analyses a class. */ class PhpdocClassAnalyser extends PhpdocAnalyser { /** * Class data. * @var array */ var $classes = array(); /** * Name of the baseclass of the given classes. * @var string */ var $baseclass = ""; /** * Ordered list of all classes. * @var string */ var $classlist = array(); /** * List of not inherited elements. * @var array */ var $notinherited = array( "class" => array( "name" => true, "extends" => true, "undoc" => true, "variables" => true, "functions" => true, "consts" => true, "uses" => true, "filename" => true, "subclasses"=> true, "path" => true, "baseclass" => true, "abstract" => true ), "functions" => array( "name" => true, "undoc" => true, "inherited" => true, "overrides" => true, "abstract" => true ), "variables" => array( "name" => true, "undoc" => true, "inherited" => true, "overrides" => true, "abstract" => true ), "uses" => array( "name" => true, "undoc" => true, "inherited" => true, "overrides" => true ), "consts" => array( "name" => true, "undoc" => true, "inherited" => true, "overrides" => true ) ); /** * Puuuh - findUndocumented() needs this. * @var array * @see findUndocumented() */ var $undocumentedFields = array( "functions" => "function", "variables" => "variable", "uses" => "included file", "consts" => "constant" ); /** * Sets the class data and the name of the baseclass. * * @param array Raw class data from the parser * @param string Name of the baseclass of the given classes * @access public */ function setClasses($classes, $baseclass) { $this->classes = $classes; $this->baseclass = $baseclass; } // end func setClasses function analyse() { $this->flag_get = false; $this->updateAccessReturn(); $this->updateBrothersSisters(); $this->classlist = array(); $this->buildBottomUpClasslist($this->baseclass); } // end func analyse /** * Returns an analysed class or false if there're no classes any more. * * @return mixed False if there no classes anymore, otherwise an array with * the data of the class. * @access public */ function getClass() { if (!$this->flag_get) { reset($this->classlist); $this->flag_get = true; } if (list($k, $classname)=each($this->classlist)) { if (isset($this->classes[$classname]["path"])) $this->inheritClassElements($classname); $this->checkFunctionArgs($classname); $this->findUndocumented($classname); $class = $this->classes[$classname]; unset($this->classes[$classname]); return $class; } else { return false; } } // end func getClass /** * Looks for undocumented elements in a certain class * * @param string Classname */ function findUndocumented($classname) { $file = $this->classes["filename"]; if ($this->classes["undoc"]) $this->warn->addDocWarning($file, "class", $name, "The class is not documented.", "missing"); reset($this->undocumentedFields); while (list($index, $eltype)=each($this->undocumentedFields)) { if (!isset($this->classes[$index])) continue; reset($this->classes[$index]); while (list($elname, $data)=each($this->classes[$index])) if (isset($data["undoc"]) && $data["undoc"]) $this->warn->addDocWarning($file, $eltype, $elname, "Undocumented element.", "missing"); } } // end func findUndocumented /** * Checks the function documentation of a certain class. * * @param string Classname */ function checkFunctionArgs($classname) { if (!isset($this->classes[$classname]["functions"])) return; $file = $this->classes[$classname]["filename"]; reset($this->classes[$classname]["functions"]); while (list($fname, $function)=each($this->classes[$classname]["functions"])) { $inherited = isset($function["paraminherited"]); $this->classes[$classname]["functions"][$fname]["params"] = $this->checkArgDocs($function["args"], $function["params"], $fname, $file, $inherited); unset($this->classes[$classname]["functions"][$fname]["args"]); if ($inherited) unset($this->classes[$classname]["functions"][$fname]["paraminherited"]); } } // end func checkFunctionArgs /** * Builds an internal list of all classes. * * The analyser needs an ordered list of all classes * to inherit information effective. * * @param string Name of the class that starts the recursive build process. * @see $classlist */ function buildBottomUpClasslist($classname) { if (isset($this->classes[$classname]["subclasses"])) { reset($this->classes[$classname]["subclasses"]); while (list($subclass, $v)=each($this->classes[$classname]["subclasses"])) $this->buildBottomUpClasslist($subclass); $this->classlist[] = $classname; } else { $this->classlist[] = $classname; } } // end func buildBottomUpClasslist /** * Adds inherited elements to a class. * * @param string Classname * @return boolean $ok * @see $classes, $notinherited, addInheritedElements() */ function inheritClassElements($classname) { if (!isset($this->classes[$classname]["path"])) return false; $undoc = $this->classes[$classname]["undoc"]; $path = $this->classes[$classname]["path"]; reset($path); while (list($k, $parentclass)=each($path)) { $this->addInheritedElements($classname, $parentclass, "functions"); $this->addInheritedElements($classname, $parentclass, "variables"); $this->addInheritedElements($classname, $parentclass, "consts"); $this->addInheritedElements($classname, $parentclass, "uses"); reset($this->classes[$parentclass]); while (list($field, $value)=each($this->classes[$parentclass])) if (!isset($this->notinherited["class"][$field]) && !isset($this->classes[$classname][$field])) $this->classes[$classname][$field] = $value; if ($undoc && !$this->classes[$parentclass]["undoc"]) { $this->classes[$classname]["docinherited"] = true; $this->classes[$classname]["undoc"] = false; $undoc = false; } } return true; } // end func inheritClassElements /** * Adds inherited functions, variables, constants or included files to a class. * * @param string Name of the class that inherits the informations. * @param string Name of the parentclass * @param string Type of elements inherited: "functions", "variables", "uses", "consts" * @return boolean $ok * @see $classes, $notinherited, isUndocumented() */ function addInheritedElements($classname, $parentclass, $type) { if (!isset($this->classes[$parentclass][$type])) return false; reset($this->classes[$parentclass][$type]); while (list($elementname, $data)=each($this->classes[$parentclass][$type])) { if (!isset($this->classes[$classname][$type][$elementname])) { $this->classes[$classname]["inherited"][$type][$parentclass][$elementname] = true; } else { $this->classes[$classname][$type][$elementname]["overrides"] = $parentclass; $this->classes[$classname][$type][$elementname]["undoc"] = $this->isUndocumented($parentclass, $type, $elementname); $this->classes[$classname]["overrides"][$type][$parentclass][$elementname] = true; reset($data); while (list($field, $value)=each($data)) { if (!isset($this->classes[$classname][$type][$elementname][$field]) && !isset($this->notinherited[$type][$field])) { $this->classes[$classname][$type][$elementname][$field] = $value; if ("params"==$field && "functions"==$type) $this->classes[$classname][$type][$elementname]["paraminherited"] = true; } } } } return true; } // end func addInheritedElements /** * Returns true if the requested element is undocumented and false if it's documented. * * The function checks if the element might inherit documentation * from any parentclass. * * @param string Name of the class of the element * @param string Element type: functions, variables, uses, consts. * @param string Element name * @return boolean $ok */ function isUndocumented($classname, $type, $elementname) { if ( !isset($this->classes[$classname][$type][$elementname]) || $this->classes[$classname][$type][$elementname]["undoc"] || !isset($this->classes[$classname]["path"]) ) return true; $path = $this->classes[$classname]["path"]; while (list($k, $parentclass)=each($path)) if ($this->isUndocumented($parentclass, $type, $elementname)) return true; return false; } // end func isUndocumented function updateBrothersSisters() { reset($this->classes); while (list($classname, $data)=each($this->classes)) { $this->updateBrotherSisterElements($classname, "functions"); $this->updateBrotherSisterElements($classname, "variables"); } } // end func updateBrothersSisters function updateBrotherSisterElements($classname, $type) { if (!isset($this->classes[$classname][$type])) return false; reset($this->classes[$classname][$type]); while (list($elementname, $data)=each($this->classes[$classname][$type])) { if (isset($data["brother"])) { $name = ( "functions" == $type ) ? substr($data["brother"], 0, -2) : substr($data["brother"], 1); $name = strtolower($name); if (!isset($this->classes[$classname][$type][$name])) { $this->warn->addDocWarning($this->classes[$classname]["filename"], $type, $elementname, "Brother '$name' is unknown. Tags gets ignored.", "mismatch"); unset($this->classes[$classname][$type][$elementname]["brother"]); } else { $this->classes[$classname][$type][$elementname]["brother"] = $name; #### PANIK ### } } } } // end func updateBrotherSisterElements function updateAccessReturn() { reset($this->classes); while (list($classname, $data)=each($this->classes)) { if (!isset($data["access"])) $this->classes[$classname]["access"] = "private"; $this->updateAccessReturnElements($classname, "functions"); $this->updateAccessElements($classname, "variables"); $this->updateAccessElements($classname, "consts"); } } // end func updateAccessReturn /** * Updates @access and @return for certain elements. * * This function should only be used to update functions. * Functions that have the same name as the class (constructors) * get @return void and @access public. Functions without * @access get @access public and functions without @return get * @return void. * * @param string Classname * @param string Element type: functions (, variables, consts, uses) * @return boolean $ok * @see updateAccessReturn() */ function updateAccessReturnElements($classname, $type) { if (!isset($this->classes[$classname][$type])) return false; reset($this->classes[$classname][$type]); while (list($elementname, $data)=each($this->classes[$classname][$type])) { if (!isset($data["access"])) $this->classes[$classname][$type][$elementname]["access"] = ("functions" == $type && strtolower($elementname) == strtolower($classname)) ? "public" : "private"; if (!isset($data["return"])) $this->classes[$classname][$type][$elementname]["return"] = "void"; else if ("functions" == $type && $elementname == $classname) { $this->warn->addDocWarning($this->classes[$classname]["filename"], "functions", $elementname, "The constructor can't have a return value. @return gets ignored.", "mismatch"); $this->classes[$classname]["functions"][$elementname]["return"] = "void"; } } } // end func updateAccessReturnElements /** * Updates @access tags. * * @param string Classname * @param string Element type: functions, variables, consts (, uses) * @see updateAccessReturnElements() */ function updateAccessElements($classname, $type) { if (!isset($this->classes[$classname][$type])) return false; reset($this->classes[$classname][$type]); while (list($elementname, $data)=each($this->classes[$classname][$type])) { if (!isset($data["access"])) $this->classes[$classname][$type][$elementname]["access"] = ("functions" == $type && $elementname == $classname) ? "public" : "private"; } } // end func updateAccessElements } // end class PhpdocClassAnalyser ?> Index: php4/pear/PHPDoc/analyser/PhpdocModuleAnalyser.php +++ php4/pear/PHPDoc/analyser/PhpdocModuleAnalyser.php <?php /** * Analyses a modulegroup. */ class PhpdocModuleAnalyser extends PhpdocAnalyser { /** * Module data * @var array */ var $modulegroup = array(); /** * List of all modules in the modulegroup * @var array */ var $modulelist = array(); /** * Puuuh - findUndocumented() needs this. * @var array * @see findUndocumented() */ var $undocumentedFields = array( "functions" => "function", "uses" => "included file", "consts" => "constant" ); /** * Sets the data of the modulegroup to analyse. * * @param array Raw modulegroup data from the parser. * @access public */ function setModulegroup($modulegroup) { $this->modulegroup = $modulegroup; } // end func setModulegroup function analyse() { $this->flag_get = false; $this->buildModulelist(); $this->updateAccessReturn(); $this->updateBrothersSisters(); $this->checkFunctionArgs(); $this->findUndocumented(); } // end func analyse /** * Returns a module from the modulegroup or false if there are no more modules. * * @return mixed False if there no more modules in the modulegroup otherwise * an array with the data of a module. * @access public */ function getModule() { if (!$this->flag_get) { reset($this->modulelist); $this->flag_get = true; } if (list($modulename, $group)=each($this->modulelist)) { $module = $this->modulegroup[$group][$modulename]; unset($this->modulegroup[$group][$modulename]); return $module; } else { return false; } } // end func getModule function findUndocumented() { reset($this->modulegroup); while (list($group, $modules)=each($this->modulegroup)) { reset($modules); while (list($name, $module)=each($modules)) { reset($this->undocumentedFields); while (list($index, $eltype)=each($this->undocumentedFields)) { if (!isset($module[$index])) continue; $file = $module["filename"]; reset($module[$index]); while (list($elname, $data)=each($module[$index])) if (isset($data["undoc"]) && $data["undoc"]) $this->warn->addDocWarning($file, $eltype, $elname, "Undocumented element.", "missing"); } } } } // end func findUndocumented function checkFunctionArgs() { reset($this->modulegroup); while (list($group, $modules)=each($this->modulegroup)) { reset($modules); while (list($name, $module)=each($modules)) { if (!isset($module["functions"])) continue; $file = $module["filename"]; reset($module["functions"]); while (list($fname, $function)=each($module["functions"])) { $this->modulegroup[$group][$name]["functions"][$fname]["params"] = $this->checkArgDocs($function["args"], $function["params"], $fname, $file, false); unset($this->modulegroup[$group][$name]["functions"][$fname]["args"]); } } } } // end func checkFunctionArgs /** * Builds an internal list of all modules in the modulegroup. * @see $modulelist, $modulegroup */ function buildModulelist() { $this->modulelist = array(); reset($this->modulegroup); while (list($group, $modules)=each($this->modulegroup)) { reset($modules); while (list($modulename, $data)=each($modules)) $this->modulelist[$modulename] = $group; } } function updateBrothersSisters() { reset($this->modulelist); while (list($modulename, $group)=each($this->modulelist)) { $this->updateBrotherSisterElements($group, $modulename, "functions"); $this->updateBrotherSisterElements($group, $modulename, "variables"); } } // end func updateBrothersSisters function updateBrotherSisterElements($group, $modulename, $type) { if (!isset($this->modulegroup[$group][$modulename][$type])) return false; reset($this->modulegroup[$group][$modulename][$type]); while (list($elementname, $data)=each($this->modulegroup[$group][$modulename][$type])) { if (isset($data["brother"])) { $name = ( "functions" == $type ) ? substr($data["brother"], 0, -2) : substr($data["brother"], 1); $name = strtolower($name); if (!isset($this->modulegroup[$group][$modulename][$type][$name])) { $this->warn->addDocWarning($this->modulegroup[$group][$modulename]["filename"], $type, $elementname, "Brother '$name' is unknown. Tags gets ignored.", "mismatch"); unset($this->modulegroup[$group][$modulename][$type][$elementname]["brother"]); } else { $this->modulegroup[$group][$modulename][$type][$elementname]["brother"] = $name; } } } } // end func updateBrotherSistersElements function updateAccessReturn() { reset($this->modulelist); while (list($modulename, $group)=each($this->modulelist)) { if (!isset($this->modulegroup[$group][$modulename]["access"])) $this->modulegroup[$group][$modulename]["access"] = "private"; $this->updateAccessReturnElements($group, $modulename, "functions"); $this->updateAccessReturnElements($group, $modulename, "variables"); $this->updateAccessElements($group, $modulename, "consts"); } } // end func updateAccessReturn function updateAccessReturnElements($group, $modulename, $type) { if (!isset($this->modulegroup[$group][$modulename][$type])) return false; reset($this->modulegroup[$group][$modulename][$type]); while (list($elementname, $data)=each($this->modulegroup[$group][$modulename][$type])) { if (!isset($data["access"])) $this->modulegroup[$group][$modulename][$type][$elementname]["access"] = "private"; if (!isset($data["return"])) $this->modulegroup[$group][$modulename][$type][$elementname]["return"] = "void"; } } // end func updateAccessReturnElements function updateAccessElements($group, $modulename, $type) { if (!isset($this->modulegroup[$group][$modulename][$type])) return false; reset($this->modulegroup[$group][$modulename][$type]); while (list($elementname, $data)=each($this->modulegroup[$group][$modulename][$type])) { if (!isset($data["access"])) $this->modulegroup[$group][$modulename][$type][$elementname]["access"] = "private"; } } // end func updateAccessElements } // end class PhpdocModuleAnalyser ?> Index: php4/pear/PHPDoc/core/Phpdoc.php +++ php4/pear/PHPDoc/core/Phpdoc.php <?php /** * Coordinates several Phpdoc Object to parse and render source files. * * @access public */ class Phpdoc extends PhpdocSetupHandler { /** * Result from the indexer * @var array $indexer * @see render() */ var $indexer_result = array(); /** * Print status messages */ var $flag_output = true; /** * Calls the command line handler if necessary. * * @global $argc, $PHP_SELF */ function Phpdoc() { global $argc, $PHP_SELF; $this->target = $PHP_SELF."apidoc/"; if ($argc>1) $this->handleArgv(); } // end constructor /** * Starts the parser. * * @return bool $ok * @throws PhpdocError * @access public * @see */ function parse() { $this->warn = new PhpdocWarning; $errors = $this->checkStatus(); if (0!=count($errors)) { reset($errors); while (list($k, $error)=each($errors)) $this->err[] = new PhpdocError($error["msg"]."Errno = ".$error["errno"], 9, __FILE__, __LINE__); return false; } $this->outl("Parser starts..."); // create some objects $fileHandler = new PhpdocFileHandler; $parser = new PhpdocParser(true); $classAnalyser = new PhpdocClassAnalyser; $moduleAnalyser = new PhpdocModuleAnalyser; $indexer = new PhpdocIndexer; $classExporter = new PhpdocXMLClassExporter(); $classExporter->setPath($this->target); $moduleExporter = new PhpdocXMLModuleExporter(); $moduleExporter->setPath($this->target); $indexExporter = new PhpdocXMLIndexExporter(); $indexExporter->setPath($this->target); $warningExporter = new PhpdocXMLWarningExporter(); $warningExporter->setPath($this->target); // This will change one fine day! $parser->warn = $this->warn; $classAnalyser->warn = $this->warn; $moduleAnalyser->warn = $this->warn; $classExporter->warn = $this->warn; $moduleExporter->warn = $this->warn; $indexer->warn = $this->warn; $sourcefiles = $fileHandler->getFilesInDirectory($this->sourceDirectory, $this->sourceFileSuffix); $parser->setPhpSourcecodeFiles($fileHandler->get($sourcefiles)); $this->outl("... preparse to find modulegroups and classtrees."); $parser->preparse(); $this->outl("... parsing classes."); while ($classtree = $parser->getClassTree()) { $classAnalyser->setClasses( $classtree, $parser->current_baseclass ); $classAnalyser->analyse(); while ($class = $classAnalyser->getClass()) { $indexer->addClass($class); $classExporter->export($class); } if (floor(phpversion())>3) { $indexExporter->exportClasstree($indexer->getClasstree(), $parser->current_baseclass); } else { $classtree = $indexer->getClasstree(); $base = $parser->current_baseclass; $indexExporter->exportClasstree($classtree, $base); } } $this->outl("... parsing modules."); while ($modulegroup = $parser->getModulegroup()) { $moduleAnalyser->setModulegroup( $modulegroup ); $moduleAnalyser->analyse(); while ($module = $moduleAnalyser->getModule()) { $indexer->addModule($module); $moduleExporter->export($module); } if (floor(phpversion())>3) { $indexExporter->exportModulegroup($indexer->getModulegroup()); } else { $modulegroup = $indexer->getModulegroup(); $indexExporter->exportModulegroup($modulegroup); } } $this->outl("... writing packagelist."); if (floor(phpversion())>3) { $indexExporter->exportPackagelist($indexer->getPackages()); $indexExporter->exportElementlist($indexer->getElementlist()); } else { $packages = $indexer->getPackages(); $indexExporter->exportPackagelist($packages); $elements = $indexer->getElementlist(); $indexExporter->exportElementlist($elements); } $warningExporter->export($parser->warn->getWarnings(), "parser"); $warningExporter->export($moduleAnalyser->warn->getWarnings(), "moduleanalyser"); $warningExporter->export($classAnalyser->warn->getWarnings(), "classanalyser"); $this->outl("Parser finished."); return true; } // end func parse /** * Renders the PHPDoc XML files as HTML files * * @param string Targetformat, currently only "html" is available. * @param string Target directory for the html files * @param string Directory with the html templates * @return bool $ok * @throws PhpdocError * @access public */ function render($type="html", $target="", $template="") { $this->outl("Starting to render..."); $target = (""==$target) ? $this->target : $this->getCheckedDirname($target); $template = (""==$template) ? $this->templateRoot : $this->getCheckedDirname($template); switch(strtolower($type)) { case "html": default: $renderer = new PhpdocHTMLRendererManager($target, $template, $this->application); break; } $fileHandler = new PhpdocFileHandler; $files = $fileHandler->getFilesInDirectory($target, "xml"); $len = strlen($target); $tpl = new IntegratedTemplate($this->templateRoot); $tpl->loadTemplateFile("xmlfiles.html"); $tpl->setCurrentBlock("file_loop"); // Do not change the file prefixes! reset($files); while (list($k, $file)=each($files)) { $tpl->setVariable("FILE", substr($file, $len)); $tpl->parseCurrentBlock(); if ("class_" == substr($file, $len, 6)) { $renderer->render(substr($file, $len), "class"); } else if ("module_" == substr($file, $len, 7)) { $renderer->render(substr($file, $len), "module"); } else if ("classtree_" == substr($file, $len, 10)) { $renderer->render(substr($file, $len), "classtree"); } else if ("modulegroup_" == substr($file, $len, 12)) { $renderer->render(substr($file, $len), "modulegroup"); } else if ("warnings_" == substr($file, $len, 9)) { $renderer->render(substr($file, $len), "warning"); } } $renderer->finish(); $fileHandler->createFile($target."phpdoc_xmlfiles.html", $tpl->get()); $this->outl($this->finishInstructions); return true; } // end func render } // end class Phpdoc ?> Index: php4/pear/PHPDoc/core/PhpdocArgvHandler.php +++ php4/pear/PHPDoc/core/PhpdocArgvHandler.php <?php /** * Handles command line arguments. * * Be careful the source has not been tested yet, it's probably very buggy. * Any help and comments are welcome... * * @author Ulf Wendel <ulf@redsys.de> * @version 0.1alpha */ class PhpdocArgvHandler extends PhpdocObject { /** * Message explaining the usage of phpdoc on the command line. * * Actually it's not the message itself but an array containing * the instructions. The array is indexed by the command line option e.g. "-h". * The array values hold a short message describing the usage of the option. * * @var string $command * @access private */ var $COMMANDS = array( "-f filename [, filename]" => "name of files to parse", "-d directory" => "name of a directory to parse", "-p path" => "path of the files", "-t target" => "path where to save the generated files, default is the current path", "-h" => "show this help message" ); /** * Handle the command line values * * handleArgv() looks for command line values and * interprets them. If there're unknown command it prints * a message and calls die() */ function handleArgv() { global $argv, $argc; // the first argv is the name of the script, // so there must be at least another one if ($argc<2) { $error = "\n\nCould not understand your request.\n\n"; $error.= $this->getArgvHelpMessage(); print $error; die(); } $commands = 0; $errors = array(); reset($argv); // skip the fist, it's the name of the php script next($argv); while (list($k, $arg)=each($argv)) { // valid command? if ("-"!=substr($arg, 0, 1)) continue; $cmd = substr($arg, 1, 2); $value = trim(substr($arg, 3)); // all command line options except -h require values if (""==$value && "h"!=$cmd) { $errors[] = array( "msg" => sprintf("-%s: no value found", trim($cmd)), "type" => "argv" ); // skip this command continue; } switch ($cmd) { case "f ": $files = explode(",", substr($arg, 3)); $this->setFiles($files); $commands++; break; case "d ": $this->setDirectory($value); $commands++; break; case "p ": $this->setPath($value); $commands++; break; case "t ": $this->setTarget($value); $commands++; break; case "h ": $commands++; break; default: $errors[]="unknown command: '$arg'"; break; } } // are there enough informations to start work? $errors = $this->checkStatus($errors); // check for errors and die() if neccessary if (count($errors)>0) { $error = "\n\nCould not understand your request.\n\n"; reset($errors); while (list($k, $data)=each($errors)) $error.=$data["msg"]."\n"; $error.= $this->getArgvHelpMessage(); print $error; die(); } // no errors, but no recognized commands? die() if neccessary if (0==$commands) { $error = "\n\nCould not understand your request.\n\n"; $error.= $this->getArgvHelpMessage(); print $error; die(); } // YEAH everything is fine, we can start working! $this->parse(); } // end func handleArgv /** * Returns the current help message of phpdoc * * The message is not HTML formated, it could be shown * on the command line. * * @access private * @return string $help_msg Some instructions on available command line options * @see handleArgv(), COMMANDS */ function getArgvHelpMessage() { $help_msg = ""; // generate the message from the COMMAND array reset($this->COMMANDS); while (list($param, $explanation)=each($this->COMMANDS)) $help_msg.= sprintf("%-28s%s\n", $param, $explanation); $help_msg.="\nFurter information can be found in the documentation.\n"; return $help_msg; } // end func getArgvHelpMessage } // end class PhpdocArgvHandler ?> Index: php4/pear/PHPDoc/core/PhpdocObject.php +++ php4/pear/PHPDoc/core/PhpdocObject.php <?php /** * Common base class of all phpdoc classes * * As a kind of common base class PhpdocObject holds * configuration values (e.g. error handling) and debugging * methods (e.g. introspection()). It does not have a constructor, * so you can always inheritig Phpdoc classes from this * class without any trouble. * * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version 1.0beta * @package PHPDoc */ class PhpdocObject { var $application = "PHPDoc"; var $warn; /** * Variable containing the latest exception object * @var object exception $err * @access public */ var $err = array(); /** * Flag determining wheter to print some status messages or not (default: false) * @var bool $flag_output * @access privte * @see setFlagOutput() * @since 0.3 */ var $flag_output = false; /** * Sets the output flag - if set to true out() and outl() print messages * @param bool $flagOutput * @access public * @see flag_output, out(), outl() * @since 0.3 */ function setFlagOutput($flagOutput) { $this->flag_output = ($flagOutput) ? true : false; } // end func setFlagOutput /** * Print a string and flushes the output buffer * @param string $message */ function out($message) { if (false == $this->flag_output) return; print $message; flush(); } // end func out /** * Encodes an element name so that it can be used as a file name. * @param string element name * @return string url name */ function nameToUrl($name) { return preg_replace("@[\s\./\\:]@", "_", $name); } // end func nameToUrl /** * Print a string, the specified HTML line break sign and flushes the output buffer * @param string $message */ function outl($message) { if (false == $this->flag_output) return; print "$message\n"; flush(); } // end func outl /** * Dumps objects and arrays. * * Use this function to get an idea of the internal datastructures used. * The function dumps arrays and objects. It renders the content in * an HTML table. Play with it, you'll see it's very helpful * for debugging. * * @param string $title Optional title used in the HTML Table * @param mixed $data Optional array or object that you want to dump. * Fallback to $this. * @param bool $userfunction Optional flag. If set to false userfunction * in an object are not shown (default). If set to * true, userfunctions are rendered * * @access public * @version 0.2 */ function introspection($title="", $data = "", $userfunction=true) { if (""==$data) $data = $this; printf('<table border="1" cellspacing="4" cellpadding="4" bordercolor="Silver">%s', $this->CR_HTML ); if (""!=$title) printf('<tr>%s<td colspan=4><b>%s</b></td>%s</tr>%s', $this->CR_HTML, $title, $this->CR_HTML, $this->CR_HTML ); reset($data); while (list($k, $v)=each($data)) { if ("user function"==gettype($v) && !$userfunction) continue; if (is_array($v) || is_object($v)) { $color="navy"; printf('<tr> <td align="left" valign="top"> <font color="%s"><pre><b>%s</b></pre></font> </td> <td align="left" valign="top"><font color="%s"><pre>=></pre></font></td> <td align="left" valign="top" colspan=2>', $color, $k, $color, str_replace("<", "&lt;", $v) ); $this->introspection("", $v, $userfunction); printf('</td>%s</tr>%s', $this->CR_HTML, $this->CR_HTML); } else { $color="black"; printf('<tr> <td align="left" valign="top"> <font color="%s"><pre><b>%s</b></pre></font> </td> <td align="left" valign="top"><pre><font color="%s">=></pre></font></td> <td align="left" valign="top"><pre><font color="%s">[%s]</font></pre></td> <td align="left" valign="top"><pre><font color="%s">"%s"</font></pre></td> </tr>', $color, $k, $color, $color, gettype($v), $color, str_replace("<", "&lt;", $v) ); } } print '</table>'.$this->CR_HTML; } // end func introspection } // end class PhpdocObject ?> Index: php4/pear/PHPDoc/core/PhpdocSetupHandler.php +++ php4/pear/PHPDoc/core/PhpdocSetupHandler.php <?php /** * Handles the "setup". * * This class provides all methods neccessary to "setup" Phpdoc and check the * current setup. * * @version 0.2alpha * @author Ulf Wendel <ulf@redsys.de> */ class PhpdocSetupHandler extends PhpdocArgvHandler { /** * Name of the target directory. * @var string $target * @access private */ var $target = ""; /** * Name of the application parsed * @var string $application * @see setApplication() */ var $application = "PHPDoc"; /** * Basedir for all file operations * @var string $basedir * @see setApplication(), setBasedir() */ var $basedir = ""; /** * Suffix of all source code files in the application * If you used other file suffixes than ".php" in you have to override this * variable using setSourceFileSuffix() * @var array sourceFileSuffix * @see setSourceFileSuffix() */ var $sourceFileSuffix = array ( "php" ); /** * Directory with the php sources to parse. * @var string * @see setSourceDir() */ var $sourceDirectory = ""; /** * Directory with the templates. * @var string * @see setTemplateDirectory() */ /** * Sets the name of the directory with the source to scan. * @param string * @access public */ function setSourceDirectory($sourcedir) { $this->sourceDirectory = $this->getCheckedDirname($sourcedir); } // end end func setSourceDirectory /** * Sets the name of the directory with the templates. * @param string * @access public */ function setTemplateDirectory($sourcedir) { $this->templateRoot = $this->getCheckedDirname($sourcedir); } // end func setTemplateDirectory /** * Sets the name of your application. * * The application name gets used on many places in the default templates. * * @param string $application name of the application * @return bool $ok * @throws PhpdocError * @access public */ function setApplication($application) { if (""==$application) { $this->err[] = new PhpdocError("No application name given.", __FILE__, __LINE__); return false; } $this->application = $application; return true; } // end func setApplication /** * Suffix of all source code files in the application * By default only files with the suffix ".php" are recognized as * php source code files and parsed. If you used other * suffixes such as ".inc" you have to tell phpdoc to parse * them. * * @param mixed $suffix string with one suffix or array of suffixes * @return bool $ok * @throws PhpdocError * @access private * @see sourceFileSuffix */ function setSourceFileSuffix($suffix) { if ( (!is_array($suffix) && ""==$suffix) || (is_array($suffix) && 0==count($suffix)) ) { $this->err[] = new PhpdocError("No suffix specified.", __FILE__, __LINE__); return false; } if (!is_array($suffix)) $suffix = array($suffix); $this->sourceFileSuffix = $suffix; return true; } // end func setSourceFileSuffix /** * Sets the target where the generated files are saved. * * @param string $target * @return bool $ok * @throws PhpdocError * @access public */ function setTarget($target) { if (""==$target) { $this->err[] = new PhpdocError("No target specified.", __FILE__, __LINE__); return false; } if (!is_dir($target)) { $ok = @mkdir($target, 0755); if (!$ok) { $this->err[] = new PhpdocError("setTarget(), can't create a directory '$target'.", __FILE__, __LINE__); return false; } } $this->target = $this->getCheckedDirname($target); return true; } // end func setTarget /** * Checks the current status of the object. Are all necessary informations * to start parsing available? * @param array $errors * @access private * @return array $errors */ function checkStatus($errors="") { if (!is_array($errors)) $errors = array(); /* if (0==count($this->files) && ""==$this->directory) $errors[] = array ( "msg" => "No source files or source directory specified.", "type" => "misconfiguration", "errno" => 6 ); if (0!=count($this->files) && ""!=$this->directory) $errors[] = array( "msg" => "Define eighter some files or a diretory.", "type" => "misconfiguration", "errno" => 7 ); */ return $errors; } // end func checkStatus /** * Adds a slash at the end of the given filename if neccessary. * * @param string Directoryname * @return string Directoryname */ function getCheckedDirname($dirname) { if (""!=$dirname && "/"!=substr($dirname, -1)) $dirname.="/"; return $dirname; } // end func getCheckedDirname } // end class PhpdocSetupHandler ?> Index: php4/pear/PHPDoc/exceptions/PhpdocError.php +++ php4/pear/PHPDoc/exceptions/PhpdocError.php <?php /** * PHPDoc Error Handling class * * PHPDoc "throws" an error class derived from this class whenever * an error occurs. PHPDoc saves the error object to the public array * $this->err[] which exists in every PHPDoc class and tries to return * a useful value that indicates that something might have gone wrong. * * The class is widely equal to the PEAR error handling class PEAR_ERROR. * * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version 1.0 * @package PHPDoc */ class PhpdocError { /** * Name of the error object class used to construct the error message * @var string $classname */ var $classname = "PhpdocError"; /** * Error message prefix. * @var string $error_message_prefix */ var $error_message_prefix = ""; /** * Error prepend, used for HTML formatting. * @var string $error_prepend */ var $error_prepend = "<b>"; /** * Error append, used for HTML formatting. * @var string $error_append */ var $error_append = "</b>"; /** * The error message itself. * * Use getMessage() to access it. * * @var string $message * @see PhpdocError */ var $message = ""; /** * File where the error occured. * @var string $file * @see PhpdocError() */ var $file = ""; /** * Line number where the error occured. * @var int $line * @see PhpdocError() */ var $line = 0; /** * Array that describes how an error gets handled. * @var array $errorHandling * @see PhpdocError() */ var $errorHandling = array( "print" => false, "trigger" => false, "die" => false ); /** * Sets the error message, filename and linenumber. * @param string Errormessage * @param string Name of the file where the error occured, use __FILE__ for this * @param string Linenumber where the error occured, use __LINE__ for this */ function PhpdocError($message, $file, $line) { $this->message = $message; $this->file = $file; $this->line = $line; if ($this->errorHandling["print"]) $this->printMessage(); if ($this->errorHandling["trigger"]) trigger_error($this->getMessage(), "E_USER_NOTICE"); if ($this->errorHandling["die"]) die($this->getMessage); } // end func PhpdocError /** * Returns a string with the error message. * @access public */ function getMessage() { return sprintf("%s%s: %s [File: %s, Line: %s]%s", $this->error_prepend, $this->error_message_prefix, $this->message, $this->file, $this->line, $this->error_append); } // end func getMessage /** * Prints the error message. * @brother getMessage() */ function printMessage() { print $this->getMessage(); } // end func printMessage } // end class PhpdocError ?> Index: php4/pear/PHPDoc/filehandler/PhpdocFileHandler.php +++ php4/pear/PHPDoc/filehandler/PhpdocFileHandler.php <?php /** * File handling functions in phpdoc. * * @version 0.1alpha * @author Ulf Wendel <ulf@redsys.de> */ class PhpdocFileHandler extends PhpdocObject { /** * Filepath. The path is automatically added in front of all filenames * @var string $path * @see setFilePath() * @access private */ var $path = ""; /** * Reads a list of files or one file. * @param string $files filename * @param array $files list of filenames, $k => $filename * @return array $contents file contents indexed by filename * @throws PhpdocError * @access public */ function Get($files) { if (""==$files) { $this->err[] = new PhpdocError("No files specified.", __FILE__, __LINE__); return array("", ""); } if (!is_array($files)) $files = array($files); $contents = array(); $together = ""; reset($files); while (list($k, $filename)=each($files)) $contents[$filename] = $this->getFile($filename); return $contents; } // end func Get /** * Sets the filepath. The path is automatically added in front of all filenames * @param string $path * @return bool $ok * @access public */ function setFilePath($path) { $this->path = $path; } // end func setFilePath /** * Reads a file. * @param string $filename * @return string $content * @throws PhpdocError * @access private */ function getFile($filename) { if (""==$filename) { $this->err[] = new PhpdocError("getFile(), no filename specified.", __FILE__, __LINE__); return ""; } if (!file_exists($filename)) { $this->err[] = new PhpdocError("getFile(), unknown file '$filename'.", __FILE__, __LINE__); return ""; } if (!$fh = @fopen($filename, "r")) { $this->err[] = new PhpdocError("getFile(), can't open file '$filename' for reading.", __FILE__, __LINE__); return ""; } $content = fread($fh, filesize($filename)); fclose($fh); return $content; } // end func getFile function appendToFile($filename, $content, $directory="") { if (""==$filename || ""==$content) { $this->err[] = new PhpdocError("No filename and/or no content given.", __FILE__, __LINE__); return false; } $fh = @fopen($filename, "a"); if (!$fh) { print $filename; ### PANIK return false; } fwrite($fh, $content); fclose($fh); return true; } // end func appendToFile /** * Creates a new file. * * Create or overrides a file in a specified directory. If the * directory does not exists, it attempts to create it. */ function createFile($filename, $content, $directory="") { if (""==$filename || ""==$content) { $this->err[] = new PhpdocError("No filename or no content given.", __FILE__, __LINE__); return false; } $fh = @fopen($filename, "w"); if (!$fh) { $this->err[] = new PhpdocError("Can't create file '$filename'.", __FILE__, __LINE__); return false; } fwrite($fh, $content); fclose($fh); return true; } // end func createFile /** * Returns a list of files in a specified directory * @param string $directory * @param mixed $suffix Suffix of the files returned * @param boolean $flag_subdir include subdirectories? * @param array $files New entries are added to this variable if provided. * Used only for the subdir feature. * @return array $files * @throws PhpdocError */ function getFilesInDirectory($directory, $suffix="", $flag_subdir = true, $files = "") { if (""==$directory) { $this->err[] = new PhpdocError("No directory specified", __FILE__, __LINE__); return array(); } if ("/"!=substr($directory, -1)) $directory.= "/"; if (""==$suffix) $flag_all = true; else { $flag_all = false; $allowed = array(); if (!is_array($suffix)) $suffix = array($suffix); reset($suffix); while (list($k, $v)=each($suffix)) $allowed[".".$v] = true; } if (!is_array($files)) $files = array(); $dh = @opendir($directory); if (!$dh) { $this->err[] = new PhpdocError("Can't open '$directory' for reading.", __FILE__, __LINE__); return array(); } while ($file = readdir($dh)) { if ("." == $file || ".." == $file) continue; if ($flag_subdir && is_dir($directory.$file)) $files = $this->getFilesInDirectory($directory.$file, $suffix, true, $files); if (!is_file($directory.$file)) continue; if ($flag_all) { $files[] = $file; } else { if (isset($allowed[substr($file, strrpos($file, "."))])) $files[] = $directory.$file; } } closedir($dh); return $files; } // end fun getFilesInDirectory } // end class PhpdocFileHandler ?> Index: php4/pear/PHPDoc/indexer/PhpdocIndexer.php +++ php4/pear/PHPDoc/indexer/PhpdocIndexer.php <?php /** * Builds Indexlists with the result from the * * @author Ulf Wendel <ulf.wendel@redsys.de> * @version 0.3alpha */ class PhpdocIndexer extends PhpdocObject { /** * Array of all packages. * @var $packages */ var $packages = array(); /** * Current classtree. * @var array */ var $classtree = array(); /** * Current modulegroup. * @var array */ var $modulegroup = array(); /** * Array of all elements (functions, variables, constant, included files, classes, packages). * @var */ var $elements = array(); /** * Array of fields that get added to the elementlist * @var array */ var $elementFields = array("functions", "variables", "consts", "uses"); /** * Adds a class to the index lists (elements, packages, classtree). * * @param array * @access public * @see addModule() */ function addClass(&$class) { $package = isset($class["package"]) ? $class["package"] : "No Package specified"; $this->packages[$package]["classes"][] = $class["name"]; $this->classtree[$class["name"]] = (isset($class["subclasses"])) ? $class["subclasses"] : array(); $this->addElements($class, "class"); } // end func addClass /** * Adds a module to the index lists (elements, packages, classtree). * * @param array * @access public * @see addClass() */ function addModule(&$module) { $package = isset($module["package"]) ? $module["package"] : "No Package specified"; $this->packages[$package]["modules"][] = $module["name"]; $this->modulegroup[$module["group"]][] = $module["name"]; $this->addElements($module, "module"); } // end func addModule /** * Returns the current classtree and resets the internal classtree field. * * @access public * @return array $classtree */ function getClasstree() { $data = $this->classtree; $this->classtree = array(); return $data; } // end func getClasstree /** * Returns the current modulegroup and resets the internal modulegroup field. * * @access public * @return array $modulegroup */ function getModulegroup() { $data = $this->modulegroup; $this->modulegroup = array(); return $data; } // end func getModulegroup /** * Returns the package list and resets the internal package field. * * @access public * @return array $packages */ function getPackages() { reset($this->packages); while (list($package, )=each($this->packages)) $this->elements[substr($package, 0, 1)][$package][] = array( "type" => "package", "sdesc" => "", "source" => "", "sourcetype" => "" ); $data = $this->packages; $this->packages = array(); return $data; } // end func getPackages /** * Returns the element index list and resets the internal elements field. * * @access public * @return array $elements */ function getElementlist() { $data = $this->elements; $this->elements = array(); return $data; } // end func getElementlist /** * Adds an element to the elementlist. * @param array * @param string Element type: class, module. */ function addElements(&$elements, $type) { $index = substr($elements["name"], 0, 1); $elname = $elements["name"]; $this->elements[$index][$elname][] = array( "type" => $type, "sdesc" => (isset($elements["sdesc"])) ? $elements["sdesc"] : "", "source" => "", "sourcetype" => $type ); reset($this->elementFields); while (list($k, $field)=each($this->elementFields)) { if (!isset($elements[$field])) continue; reset($elements[$field]); while (list($name, $data)=each($elements[$field])) { if ("variables" == $field) { $index = substr($data["name"], 1, 1); $name = $data["name"]; } else if ("uses" == $field) { $index = substr($data["file"], 0, 1); $name = $data["file"]; } else { $index = substr($data["name"], 0, 1); $name = $data["name"]; } $this->elements[$index][$name][] = array( "type" => $field, "sdesc" => (isset($data["sdesc"])) ? $data["sdesc"] : "", "source" => $elname, "sourcetype" => $type ); } } } // end func addElements } // end class PhpdocIndexer ?> Index: php4/pear/PHPDoc/parser/PhpdocClassParser.php +++ php4/pear/PHPDoc/parser/PhpdocClassParser.php <?php /** * Parses phpcode to extract classes and their documentation. * * @version 0.2 * @author Ulf Wendel, ulf@redsys.de */ class PhpdocClassParser extends PhpdocFunctionParser { /** * Array of all classes in the given code * * The array is indexed by the classname. * See $emptyClass to see the internal structure. * * @var array $classes * @see emptyClass */ var $classes = array(); /** * Default values of a class * @var array $emptyClass * @see classes, analyseClassParagraph * */ var $emptyClass = array ( "name" => "", "extends" => "", "undoc" => true ); /** * Array of tags that are allowed in front of the class keyword * @var array $classTags * @see analyseClassParagraph() */ var $classTags = array( "access" => true, "abstract" => true, "static" => true, "final" => true, "see" => true, "link" => true, "author" => true, "copyright" => true, "version" => true, "since" => true, "deprecated" => true, "deprec" => true, "brother" => true, "sister" => true, "exclude" => true, "package" => true, "magic" => true, "todo" => true ); /** * Analyse a class * * Calls all neccessary analyse functions. * * @param array * @return array */ function analyseClass($para) { $class = $this->analyseClassDoc($para["classes"][0]); reset($para["functions"]); while (list($k, $data)=each($para["functions"])) $class["functions"][strtolower($data["name"])] = $this->analyseFunction($data); unset($para["functions"]); reset($para["variables"]); while (list($k, $data)=each($para["variables"])) $class["variables"][strtolower($data["name"])] = $this->analyseVariable($data); unset($para["variables"]); reset($para["consts"]); while (list($k, $data)=each($para["consts"])) $class["consts"][strtolower($data["name"])] = $this->analyseConstant($data); unset($para["consts"]); reset($para["uses"]); while (list($k, $data)=each($para["uses"])) $class["uses"][strtolower($data["file"])] = $this->analyseUse($data); return $class; } // end func analyseClass /** * Analyses a class doc comment. * @param array Hash returned by getPhpdocParagraph() * @return array */ function analyseClassDoc($para) { $class = $this->emptyClass; $class["name"] = $para["name"]; $class["extends"] = $para["extends"]; if (""!=$para["doc"]) { $class = $this->analyseTags($this->getTags($para["doc"]), $class, $this->classTags); list($msg, $class) = $this->checkParserErrors($class, "class"); if (""!=$msg) $this->warn->addDocWarning($this->currentFile, "class", $class["name"], $msg, "mismatch"); list($class["sdesc"], $class["desc"]) = $this->getDescription($para["doc"]); $class["undoc"] = false; } return $class; } // end func analyseClassDoc } // end class PhpdocClassParser ?> Index: php4/pear/PHPDoc/parser/PhpdocConstantParser.php +++ php4/pear/PHPDoc/parser/PhpdocConstantParser.php <?php /** * Extracts define statements and their documentation from php code. * @author Ulf Wendel <ulf.wendel@redsys.de> * @version 0.1alpha */ class PhpdocConstantParser extends PhpdocUseParser { var $emptyConstant = array( "name" => "", "value" => "", "undoc" => true ); var $constantTags = array( "access" => true, "see" => true, "link" => true, "constant" => true, "const" => true, "author" => true, "copyright" => true, "exclude" => true, "magic" => true, "todo" => true ); function analyseConstant($para) { $constant = $this->emptyConstant; $constant["name"] = $para["name"]; $constant["value"] = $para["value"]; if (""!=$para["doc"]) { $constant = $this->analyseTags( $this->getTags($para["doc"]), $constant, $this->constantTags); list($msg, $constant) = $this->checkParserErrors($constant, "constant (define() keyword)"); if (""!=$msg) $this->warn->addDocWarning($this->currentFile, "constant", $constant["name"], $msg, "mismatch"); list($constant["sdesc"], $constant["desc"])=$this->getDescription($para["doc"]); $constant["undoc"] = false; } $constant = $this->checkConstantDoc($constant); if (isset($para["case"])) $constant["case"] = $para["case"]; return $constant; } // end func analyseConstant /** * Compares the data from the parser with the optional @const[ant] tags * @param array Hash with the data of the current constant paragraph * @return array $constant */ function checkConstantDoc($constant) { if (!isset($constant["const"])) { $msg = "The @const[ant] tag is missing. Add '@const ".$constant["name"]." [description]' to the tag list at the end of the doc comment."; $this->warn->addDocWarning($this->currentFile, "constant", $constant["name"], $msg, "missing"); } else { if ($constant["name"] != $constant["const"]["name"]) { $msg = sprintf("The name of the constant '%s' does not match the documented name '%s', update the tag to '@const %s %s'.", $constant["name"], $constant["const"]["name"], $constant["name"], $constant["const"]["desc"] ); $this->warn->addDocWarning($this->currentFile, "constant", $constant["name"], $msg, "mismatch"); } if (""!=$constant["const"]["desc"]) $constant["const"] = $constant["const"]["desc"]; else unset($constant["const"]); } return $constant; } // end func checkConstantDoc } // end class PhpdocConstantParser ?> Index: php4/pear/PHPDoc/parser/PhpdocFunctionParser.php +++ php4/pear/PHPDoc/parser/PhpdocFunctionParser.php <?php /** * Looks for documented and undocumented functions within a block of php code * @author Ulf Wendel <ulf@redsys.de> * @version 0.2 */ class PhpdocFunctionParser extends PhpdocVariableParser { /** * Internal structur of a function. * @var array $emptyFunction * @see functions, analyseFunctionParagraph() */ var $emptyFunction = array( "name" => "", "undoc" => true, "args" => array() ); /** * Array of tags that are allowed in front of the function keyword * @var array $functionTags * @see analyseFunctionParagraph() */ var $functionTags = array( "parameter" => true, "param" => true, "return" => true, "access" => true, "abstract" => true, "static" => true, "throws" => true, "see" => true, "link" => true, "global" => true, "version" => true, "since" => true, "deprecated" => true, "deprec" => true, "brother" => true, "sister" => true, "exclude" => true, "magic" => true, "author" => true, "copyright" => true, "todo" => true ); /** * Analyses a function doc comment. * @param array * @return array */ function analyseFunction($para) { $function = $this->emptyFunction; $function["name"] = $para["name"]; if (""!=$para["doc"]) { $function = $this->analyseTags($this->getTags($para["doc"]), $function, $this->functionTags); list($msg, $function) = $this->checkParserErrors($function, "function"); if (""!=$msg) $this->warn->addDocWarning($this->currentFile, "function", $function["name"], $msg, "mismatch"); list($function["sdesc"], $function["desc"]) = $this->getDescription($para["doc"]); $function["undoc"] = false; } $function["args"] = $this->getFunctionArgs($para["head"]); return $function; } // end func analyseFunction /** * Analyses a function head and returns an array of arguments. * @param string PHP code to examine. * @return array Array of arguments: $args[] = array( optional, default, type, name ). * @see getVariableTypeAndValue() */ function getFunctionArgs($code) { $args = array(); while (preg_match($this->PHP_COMPLEX["argument"], $code, $regs)) { $type = ""; $value = ""; $optional = false; if (!isset($regs[3])) { $len_of_value = strlen($regs[1]); } else if ("=" == $regs[3]) { $find = $regs[1].$regs[2]; $code = substr($code, strpos($code, $find)+strlen($find) ); list ($type, $value, $raw_value) = $this->getVariableTypeAndValue($code); $len_of_value = strlen($raw_value); $optional = true; } else { $len_of_value = strlen($regs[1].$regs[2]); } $code = substr($code, $len_of_value); $args[] = array( "optional" => $optional, "default" => $value, "type" => $type, "name" => $regs[1] ); } return $args; } // end func getFunctionArgs } // end class PhpdocFunctionParser ?> Index: php4/pear/PHPDoc/parser/PhpdocModuleParser.php +++ php4/pear/PHPDoc/parser/PhpdocModuleParser.php <?php /** * Extracts modules and their documentation from php code. * @author Ulf Wendel <ulf.wendel@redsys.de> * @version 0.1alpha */ class PhpdocModuleParser extends PhpdocConstantParser { /** * Empty hash that shows the structure of a module. * @var array */ var $emptyModule = array( "name" => "", "group" => "", "undoc" => true, "functions" => array(), "consts" => array(), "uses" => array() ); /** * List of tags allowed within a module doc comment. * @var array tagname => true */ var $moduleTags = array( "module" => true, "modulegroup" => true, "access" => true, "see" => true, "link" => true, "author" => true, "copyright" => true, "version" => true, "since" => true, "deprecated" => true, "deprec" => true, "brother" => true, "sister" => true, "exclude" => true, "package" => true, "magic" => true, "todo" => true ); /** * Hash of all module groups * @var array */ var $moduleGroups = array(); /** * Central module parsing function. * * @param array Array of parsing data * @return array * @see analyseModuleDoc() */ function analyseModule($para) { $module = $this->analyseModuleDoc($para["modules"]); unset($para["modules"]); $this->moduleGroups[$module["group"]][] = $module["name"]; reset($para["functions"]); while (list($k, $data)=each($para["functions"])) $module["functions"][strtolower($data["name"])] = $this->analyseFunction($data); unset($para["functions"]); reset($para["consts"]); while (list($k, $data)=each($para["consts"])) $module["consts"][strtolower($data["name"])] = $this->analyseConstant($data); unset($para["const"]); reset($para["uses"]); while (list($k, $data)=each($para["uses"])) $module["uses"][strtolower($data["file"])] = $this->analyseUse($data); return $module; } // end func analyseModule /** * Extracts the allowed documentation tags out of a module doc comment. * * @param array Module paragraph * @return array */ function analyseModuleDoc($para) { $module = $this->emptyModule; $module["name"] = (""!=$para["name"]) ? $para["name"] : $this->currentFile; $module["group"] = (""!=$para["group"]) ? $para["group"] : $this->currentFile; if ("missing" == $para["status"]) { $msg = "The file '$this->currentFile' does not contain any classes and seems to lack a module doc comment."; $this->warn->addDocWarning($this->currentFile, "module", $module["name"], $msg, "missing"); } else if ("tags missing" == $para["status"]) { $msg = "The module doc comment does not contain a @module or @modulegroup tag, the module gets names: '$this->currentFile'"; $this->warn->addDocWarning($this->currentFile, "module", $module["name"], $msg, "missing"); } if (""!=$para["doc"]) { $tags = $this->getTags($para["doc"]); $module = $this->analyseTags($tags, $module, $this->moduleTags); list($msg, $module) = $this->checkParserErrors($module, "module"); if (""!=$msg) $this->warn->addDocWarning($this->currentFile, "module", $module["name"], $msg, "mismatch"); list($shortdesc, $fulldesc) = $this->getDescription($para["doc"]); $module["sdesc"] = $shortdesc; $module["desc"] = $fulldesc; $module["undoc"] = false; } unset($module["module"]); unset($module["modulegroup"]); return $module; } // end analyseModuleDoc } // end class PhpdocModuleParser ?> Index: php4/pear/PHPDoc/parser/PhpdocParser.php +++ php4/pear/PHPDoc/parser/PhpdocParser.php <?php /** * "Main" class of the Parser collection. * * Note that a lot of communication is done by using shared instance variables. */ class PhpdocParser extends PhpdocClassParser { /** * Name of the file currently parsed. * * Instead of passing the name of the current file by argument * PHPDoc uses this slot to communicate. Yeah I know, it's * the way methods should communicate, but it saves me a lot * a lot of work. * @var string Name of the file currently parsed. */ var $currentFile = ""; /** * Array of PHP Sourcecode Files to examine. * The array keys hold the filenames, the array values the file content. * @var array * @see parse() */ var $phpfiles = array(); /** * Mapping from classnames to filenames * @var array */ var $classnamesToFilenames = array(); /** * Hash with the data of the current class tree (one parentclass with all children). * @var array * @see modules */ var $classes = array(); /** * List of all parentclasses found. * @var array */ var $baseclasses = array(); /** * List of all files containing classes. * @var array */ var $classfiles = array(); /** * Hash of all class trees. * @var array */ var $classtree = array(); /** * List of all files containing modules. * @var array */ var $modulefiles = array(); /** * List of all module groups. * @var array */ var $modulegroups = array(); /** * Hash with the data of the current module group. * @var array * @see classes */ var $modules = array(); /** * Hash of all packages found. * @var array */ var $packages = array(); /** * Flag indicating that getClassTree() was called. * @var boolean * @see getClassTree() */ var $flag_classtree = false; /** * Flag indicating that getModulegroup was called. * @var boolean * @see getModulegroup */ var $flag_modulegroup = false; /** * Name of the base class of the current class tree * @var string * @see getClassTree() */ var $current_baseclass = ""; /** * Creates an instance of PhpdocWarning and calls buildComplexRegExps() to initialize the object. * @param boolean If true the parser prints status messages. * @see $warn, buildComplexRegExps() */ function PhpdocParser($flag_output = false) { if ($flag_output) $this->setFlagOutput(true); else $this->setFlagOutput(false); $this->buildComplexRegExps(); } // end constructor /** * Central parsing function. * * With version 0.3alpha PHPdoc changed the way the parser works. It does now * 1 1/2 parsing runs. One prescan to build the class trees and a list of module * groups and one deep scan to extract the information. This reduces the memory * consumption. * * @return boolean $ok * @access public * @see findModulegroups(), findClassTrees(), getModulesAndClasses() */ function preparse() { if (0==count($this->phpfiles)) { $this->err[] = new PHPDocError("Can't parse - no files defined.", __FILE__, __LINE__); return false; } $para = array(); reset($this->phpfiles); while (list($filename, $phpcode)=each($this->phpfiles)) $para[$filename] = $this->getModulesAndClasses($phpcode); $this->findModulegroups($para); $this->findClassTrees($para); return true; } // end func preparse /** * Returns the data of one parentclass and all it's subclasses or false. * * Use this function to loop through the class trees. The loop should look somewhat like: * <code>while ( $classtree = $parser->getClassTree() ) ...</code> * * @return mixed $classes Hash with the data of the current class tree or false. * @access public * @see getModulegroup(), %baseclasses */ function getClassTree() { // first call, reset the baseclass array pointer if (!$this->flag_classtree) { reset($this->baseclasses); $this->flag_classtree = true; } if (list($classname, $filename)=each($this->baseclasses)) { $this->classes = array(); $this->current_baseclass = $classname; $this->addClass($classname, $filename); return $this->classes; } else { return false; } } // end func getClassTree /** * Returns the data of one module group. * * Use this function to loop through the module groups. The loop should look somewhat like: * <code>while ( $modulegroup = $parser->getModulegroup() ) ...</code>. * * @return mixed $modulegroup Hash with the data of the current class tree or false. * @access public * @see getClassTree(), addModule(), $modulegroups */ function getModulegroup() { if (!$this->flag_modulegroup) { reset($this->modulegroups); $this->flag_modulegroup = true; } if (list($group, $modules)=each($this->modulegroups)) { $this->modules = array(); while (list($modulename, $files)=each($modules)) { reset($files); while (list($k, $filename)=each($files)) $this->addModule($group, $filename); } return $this->modules; } else { return false; } } // end func getModulegroup /** * Analyses the given file and adds the result to the module list. * * The function analyses the given file, unsets the file in the * file list, adds the result of the parser to the module list and * if necessary it adds some data to the package list. * * @param string Name of the module group the parsing result gets added. * @param string Name of the file to scan. * @see getPhpdocParagraphs(), analyseModule() */ function addModule($group, $filename) { $data = $this->getPhpdocParagraphs($this->phpfiles[$filename], array("classes", "variables") ); // free memory as soon as possible... unset($this->phpfiles[$filename]); // note: not passed by argument $this->currentFile = $filename; $result = $this->analyseModule($data); $result["filename"] = $filename; $this->modules[$group][$result["name"]] = $result; if (isset($result["package"])) $this->packages[$result["package"]]["modules"][] = array ( "name" => $result["name"], "group" => $result["group"], "filename" => $filename ); } // end func addModule /** * Analyses the given file and adds the result to the class list. * * The first parameter (classname) comes from the prescan done * by findClassTrees() * * @param string Name of the class that gets added. * @param string Name of the file to scan. * @see addSubclasses(), analyseClass(), $classes */ function addClass($classname, $filename) { $data = $this->getPhpdocParagraphs($this->phpfiles[$filename], array("modules") ); // free memory as soon as possible... unset($this->phpfiles[$filename]); $this->currentFile = $filename; $result = $this->analyseClass($data); // Add some informations from the classtree that was build by the prescan to the class. $fields = array("subclasses", "noparent", "path", "baseclass"); reset($fields); while (list($k, $field)=each($fields)) if (isset($this->classtree[$filename][$classname][$field])) $result[$field] = $this->classtree[$filename][$classname][$field]; $result["filename"] = $filename; $this->classes[$classname] = $result; $this->addSubclasses($classname); if (isset($result["package"])) $this->packages[$result["package"]]["classes"][] = $classname; } // end func addClass /** * Adds recursively subclasses to the specified class. * @param string Name of the class that might contain subclasses * @see addClass() */ function addSubclasses($classname) { if (isset($this->classes[$classname]["subclasses"])) { $subclasses = $this->classes[$classname]["subclasses"]; while (list($subclass, $v)=each($subclasses)) $this->addClass($subclass, $this->classnamesToFilenames[$subclass]); } } // end func addSubclasses /** * Builds the hash of module groups and the module file list. * * @param array Hash with the result of getClassesAndModules() of all files * @see parse(), findClassTree(), $modulegroups, $modulefiles */ function findModulegroups($para) { reset($para); while (list($filename, $data)=each($para)) { if (isset($data["modules"]["name"])) { $name = (""!=$data["modules"]["name"]) ? $data["modules"]["name"] : $filename; $group = (""!=$data["modules"]["group"]) ? $data["modules"]["group"] : $name; if (0!=count($data["classes"])) { // As we do not have a real parser that returns a parsing tree we can't // handle modules and classes in one file. Drop a note to the user. $this->warn->addDocWarning( $filename, "module", $name, "PHPDoc is confused: module files must not contain classes. Doc will probably be broken, module gets ignored.", "collision" ); continue; } if (isset($this->modulegroups[$group][$name])) $this->warn->addDocWarning($filename, "module", $name, "Warning: there's more than one module '$name' (file: '$filename) in the module group '$group'.", "warning"); $this->modulegroups[$group][$name][] = $filename; $this->modulefiles[] = $filename; } } } // end func findModulegroups /** * Builds a hash of all class trees. * * @param array Hash with the result of getClassesAndModules() of all files * @see parse(), findModulegroups(), $classnamesToFilenames, $classtree, $classfiles, $baseclasses */ function findClassTrees($para) { reset($para); while(list($filename, $data)=each($para)) { if (0!=count($data["classes"])) { $classname = $data["classes"][0]["name"]; if (1<count($data["classes"])) $this->warn->addDocWarning($filename, "class", $classname , "PHPDoc is confused: there is more than one class in this file. Doc will probably be broken, first class '$classname' gets used, file '$filename' get ignored.", "collision"); if (isset($data["modules"]["name"])) $this->warn->addDocWarning($filename, "class", "", "Warning: found a module comment in a class file. Module comment gets ignored, doc might be broken.", "collision"); $this->classnamesToFilenames[$classname] = $filename; $this->classtree[$filename][$classname] = $data["classes"][0]; $this->classfiles[] = $filename; } } reset($this->classnamesToFilenames); while (list($classname, $filename)=each($this->classnamesToFilenames)) { $path = array(); $baseclass = $classname; $basefile = $filename; $flag_noparent = false; while ($extends = $this->classtree[$basefile][$baseclass]["extends"]) { if (!isset($this->classnamesToFilenames[$extends])) { $flag_noparent = true; break; } $this->classtree[$this->classnamesToFilenames[$extends]][$extends]["subclasses"][$baseclass] = true; $path[] = $extends; $baseclass = $extends; $basefile = $this->classnamesToFilenames[$baseclass]; } if ($flag_noparent) $this->classtree[$filename][$classname]["noparent"] = $flag_noparent; $base = (0==count($path)) ? true : false; if ($base) $this->baseclasses[$classname] = $filename; else $this->classtree[$filename][$classname]["path"] = $path; if ($baseclass != $classname) $this->classtree[$filename][$classname]["baseclass"] = $baseclass; } } // end func findClassTrees /** * Returns the mapping array from classnames to filenames * @return array $this->classnamesToFilenames * @see classnamesToFilenames */ function getClassnamesToFilenames() { return $this->classnamesToFilenames; } // end func getClassnamesToFilenames /** * Sets the list of PHP Soucecode Files to examine. * @param array $phpfiles * @return bool $ok * @access public */ function setPhpSourcecodeFiles($phpfiles) { if (!is_array($phpfiles) || 0==count($phpfiles)) return false; $this->phpfiles = $phpfiles; return true; } // end func setPhpSourcecodeFiles } // end class PhpdocParser ?> Index: php4/pear/PHPDoc/parser/PhpdocParserCore.php +++ php4/pear/PHPDoc/parser/PhpdocParserCore.php <?php /** * Provides basic parser functions. * * Provides basic parser functions to extract doc comments, analyse tags and variable * declarations. * * @author Ulf Wendel <ulf@redsys.de> * @version 0.3 */ class PhpdocParserCore extends PhpdocParserTags { /** * Scans code for documented and undocumented phpdoc keywords (classes, functions, class variables, uses, constants). * * This method is somewhat the heart of the phpdoc parser. It takes a string of * phpcode and extracts all classes, functions, class variables, uses (include and friends), * and constants (define) from it. Extract does not mean that the whole class or another element * gets extracted. It does not take the code from the class definition and it's opening * curly brace to the closing one. PHPDoc just extracts the class definition itself and * if available a trailing doc comment. This has some drawbacks: phpdoc can't handle * files that contain more than one class it wouldn't know which method/class variable belongs to * a certain class. It's possible to provide a workaround but phpdoc would slow down dramatically. * As PHPDoc does not have a real parser but does a simple grep using a bunch of regular expressions * there're indeed more limitations. Nevertheless I doubt that you'll have problems with "normal" code. * * The search algorithm looks pretty strange but belive me it's fast. I have tried several other ways * (really complex regexps >500 chars, preg_match_all + looking backwards for comments, ...) but none was * faster. This one takes 13s on my machine to scan the current (14/08/2000) code (7130 lines), the * big RegExp way took more than 5 Minutes, the preg_match_all + looking backwards 52s. * * @param string PHP code to scan. * @param mixed String of one keyword or array of keywords not to scan for. Known keywords are: * "classes", "functions", "variables", "uses", "consts". * @return array Hash of phpdoc elements found, indexed by "variables", "functions", "classes", "consts", "uses". * @see $PHP_BASE, $PHP_COMPLEX, $C_BASE, $C_COMPLEX, extractPhpdoc(), getModuleDoc() */ function getPhpdocParagraphs($phpcode, $keywords="none") { // what are we not looking for? if ( !is_array($keywords) ) { if ("none" == $keywords) $keywords = array (); else $keywords = array ( $keywords => true ); } $start = 0; $paragraphs = array( "classes" => array(), "functions" => array(), "variables" => array(), "consts" => array(), "uses" => array(), "modules" => array() ); // remember the documented elements to be able to compare with the list of all elements $variables = array(); $functions = array(); $variables = array(); $constants = array(); $uses = array(); // // Module docs are somewhat more difficult to grep. Always // use this function. // if (!isset($keywords["modules"])) list($paragraphs["modules"], $phpcode) = $this->getModuleDoc($phpcode); else list( , $phpcode) = $this->getModuleDoc($phpcode); // // Find documented elements // while (true) { $start = strpos($phpcode, "/**", $start); if (0==(int)$start && "integer" != gettype($start) ) break; $end = strpos($phpcode, "*/", $start); $remaining = trim(substr($phpcode, $end+2)); if ( !isset($keywords["classes"]) && preg_match($this->PHP_COMPLEX["class"], $remaining, $regs) || preg_match($this->PHP_COMPLEX["class_extends"], $remaining, $regs)) { $paragraphs["classes"][] = array( "name" => $regs[1], "extends" => (isset($regs[2])) ? $regs[2] : "", "doc" => $this->extractPhpdoc(substr($phpcode, $start+3, ($end-$start)-2)) ); $classes[$regs[1]] = true; } else if ( !isset($keywords["functions"]) && preg_match($this->PHP_COMPLEX["function"], $remaining, $regs)) { $head = substr($remaining, strpos($remaining, $regs[0])+strlen($regs[0])); $head = substr( trim($this->getValue($head, array( "{" => true) )), 0, -1); $paragraphs["functions"][] = array( "name" => $regs[1], "doc" => $this->extractPhpdoc( substr($phpcode, $start+3, ($end-$start)-2) ), "head" => $head ); $functions[$regs[1]] = true; } else if ( !isset($keywords["variables"]) && preg_match($this->PHP_COMPLEX["var"], $remaining, $regs)) { if ("=" == $regs[2]) $value = trim($this->getValue( substr($remaining, strpos($remaining, $regs[0])+strlen($regs[0]) ), array( ";" => true))); else $value = ""; $paragraphs["variables"][] = array( "name" => $regs[1], "value" => $value, "doc" => $this->extractPhpdoc(substr($phpcode, $start+3, ($end-$start)-2)) ); $variables[$regs[1]] = true; } else if ( !isset($keywords["consts"]) && preg_match($this->PHP_COMPLEX["const"], $remaining, $regs) ) { $name = (""!=$regs[2]) ? substr($regs[1], 1, -1) : $regs[1]; if (isset($regs[5])) { if ($regs[5]) $case = "case insensitive, userdefined: '$regs[5]'"; else $case = "case sensitive, userdefined: '$regs[5]'"; } else { $case = "default: case sensitive"; } $paragraphs["consts"][] = array( "name" => $name, "value" => (""!=$regs[4]) ? substr($regs[3], 1, -1) : $regs[3], "case" => $case, "doc" => $this->extractPhpdoc(substr($phpcode, $start+3, ($end-$start)-2)) ); $constants[$name] = true; } else if ( !isset($keywords["uses"]) && preg_match($this->PHP_COMPLEX["use"], $remaining, $regs)) { $filename = isset($regs[5]) ? $regs[5] : $regs[4]; $paragraphs["uses"][] = array( "type" => $regs[1], "file" => $filename, "doc" => $this->extractPhpdoc(substr($phpcode, $start+3, ($end-$start)-2)) ); $uses[$filename] = true; } $start++; } // // Find undocumented elements // if (!isset($keywords["classes"])) { preg_match_all($this->PHP_COMPLEX["undoc_class"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) if (!isset($classes[$data[1]])) $paragraphs["classes"][] = array( "name" => $data[1], "extends" => "", "doc" => "" ); preg_match_all($this->PHP_COMPLEX["undoc_class_extends"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) if (!isset($classes[$data[1]])) $paragraphs["classes"][] = array( "name" => $data[1], "extends" => $data[2], "doc" => "" ); } if (!isset($keywords["functions"])) { preg_match_all($this->PHP_COMPLEX["undoc_function"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) if (!isset($functions[$data[1]])) { $head = substr($phpcode, strpos($phpcode, $data[0])+strlen($data[0])); $head = substr( trim( $this->getValue($head, array( "{" => true) )), 0, -1); $paragraphs["functions"][] = array( "name" => $data[1], "doc" => "", "head" => $head ); } } if (!isset($keywords["variables"])) { preg_match_all($this->PHP_COMPLEX["undoc_var"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) if (!isset($variables[$data[1]])) { if ("=" == $data[2]) $value = trim($this->getValue( substr($phpcode, strpos($phpcode, $data[0])+strlen($data[0]) ), array( ";" => true))); else $value = ""; $paragraphs["variables"][] = array( "name" => $data[1], "value" => $value, "doc" => "" ); } } if (!isset($keywords["consts"])) { preg_match_all($this->PHP_COMPLEX["undoc_const"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) { $name = (""!=$data[2]) ? substr($data[1], 1, -1) : $data[1]; if (!isset($constants[$name])) { if (isset($data[5])) { if ($data[5]) $case = "case insensitive, userdefined: '$data[5]'"; else $case = "case sensitive, userdefined: '$data[5]'"; } else { $case = "default: case sensitive"; } $paragraphs["consts"][] = array( "name" => $name, "value" => (""!=$data[4]) ? substr($data[3], 1, -1) : $data[3], "case" => $case, "doc" => "" ); } } } if (!isset($keywords["uses"])) { preg_match_all($this->PHP_COMPLEX["undoc_use"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) { $filename = isset($data[5]) ? $data[5] : $data[4]; if (!isset($uses[$filename])) { $paragraphs["uses"][] = array( "type" => $data[1], "file" => $filename, "doc" => "" ); } } } return $paragraphs; } // end func getPhpdocParagraphs /** * Does a quick prescan to find modules an classes. * @param string Code to scan * @return array Hash of modules and classes found in the given code * @access public * @see getPhpdocParagraphs() */ function getModulesAndClasses($phpcode) { $para = array(); list( $para["modules"], $phpdcode) = $this->getModuleDoc($phpcode); $para["classes"] = $this->getClasses($phpcode); return $para; } // end func getModulesAndClasses /** * Tries to extract a module doc. * * The syntax for modules is not final yet. The implementation and meaning of "module" * might change at every time! Please do not ask for implementation details. * * @param string PHP Code to scan * @return array $module $module[0] = array with module data, * $module[1] = php code without the leading module doc */ function getModuleDoc($phpcode) { $module = array(); if (preg_match($this->C_COMPLEX["module_doc"], $phpcode, $regs) ) { $start = strlen($regs[0]); $end = strpos($phpcode, "*/", $start); $remaining = substr($phpcode, $end+2); $doc_comment= substr($phpcode, $start, $end-$start); // Do we have OO Code? If not, continue. if ( !preg_match($this->PHP_COMPLEX["class"], $remaining) && !preg_match($this->PHP_COMPLEX["class_extends"], $remaining) ) { // Is there a module tag? if ( preg_match($this->C_COMPLEX["module_tags"], $doc_comment) ) { $doc_comment = $this->extractPhpDoc($doc_comment); $tags = $this->getTags( $doc_comment); $allowed = array ( "module" => true, "modulegroup" => true ); $tags = $this->analyseTags( $tags, array(), array( "module" => true, "modulegroup" => true) ); $module = array ( "doc" => $doc_comment, "status" => "ok", "name" => (isset($tags["module"])) ? $tags["module"] : "", "group" => (isset($tags["modulegroup"])) ? $tags["modulegroup"] : "" ); } else { // No module tag. // Try the remaining keywords. If one matches it's not a module doc // assume that the module doc is missing. If none matches assume that // it's a module doc which lacks the module tags. if ( preg_match($this->PHP_COMPLEX["function"], $remaining) || preg_match($this->PHP_COMPLEX["use"], $remaining) || preg_match($this->PHP_COMPLEX["const"], $remaining) || preg_match($this->PHP_COMPLEX["var"], $remaining) ) { $module = array( "doc" => "", "status" => "missing", "name" => "", "group" => "" ); $remaining = $phpcode; } else { $module = array ( "doc" => $doc_comment, "status" => "tags missing", "name" => "", "group" => "" ); } } // end if module_tags } else { $remaining = $phpcode; } // end if class } else { $remaining = $phpcode; } return array($module, $remaining); } // end func getModuleDoc /** * Returns a list of classes found in the given code. * * In early versions PHPdoc parsed all the code at once which restulted in huge * memory intensive hashes. Now it scans for classes, builds a classtree and * does the parsing step by step, writing information to the destination * (renderer, exporter) as soon as possible. This reduces the memory consumption * dramatically. getPhpdocParagraphs() could be used to extract the class definitions * as well but this specialized function is somewhat faster. * * @param string PHP code to scan. * @return array $classes Array of classes found in the code. $classes[classname] = extends */ function getClasses($phpcode) { $classes = array(); preg_match_all($this->PHP_COMPLEX["undoc_class"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) $classes[] = array( "name" => $data[1], "extends" => "" ); preg_match_all($this->PHP_COMPLEX["undoc_class_extends"], $phpcode, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) $classes[] = array( "name" => $data[1], "extends" => $data[2] ); return $classes; } // end func getClasses /** * Strips "/xx", "x/" and x from doc comments (x means asterix). * @param string Doc comment to clean up. * @return string $phpdoc */ function extractPhpdoc($paragraph) { $lines = split( $this->PHP_BASE["break"], $paragraph); $phpdoc = ""; reset($lines); while (list($k, $line)=each($lines)) { $line = trim($line); if (""==$line) continue; if ("*" == $line[0]) $phpdoc.= trim(substr($line, 1))."\n"; else $phpdoc.= $line."\n"; } return substr($phpdoc, 0, -1); } // end func extractPhpdoc /** * Extract the description from a PHPDoc doc comment. * * Every PHPDoc doc comment has the same syntax: /xx[break][x]short description * [break][[x]multiple line long description[break]][[x]@list of tags[. This function * returns an array of the short description and long description. * * @param string Doc comment to examine. * @return array $description $description[0] = short description (first line), * $description[1] = long description (second line upto the first tag) */ function getDescription($phpdoc) { $positions = $this->getTagPos($phpdoc); if (0==count($positions)) { $desc = trim($phpdoc); $description = array ($desc, $desc); } else { $desc = trim(substr($phpdoc, 0, $positions[0]["pos"])); $lines = split($this->PHP_BASE["break"], $phpdoc); if (1==count($lines) || ""==$desc) $description = array ($desc, $desc); else $description = array ( trim($lines[0]), $desc ); } return $description; } // end func getDescription /** * Scans a code passage for a value. * * There some cases where you can hardly use a regex to grep a value * because the value might contain unescaped charaters that end the value. * Value means something like "array ( ";", '\;' );" or "'phpdoc; ';" where * the delimiter would be ";". * * @param string The php code to examine. * @param mixed String of one delimiter or array of delimiters. * @return string Value found in the code * @todo Racecondition: comments */ function getValue($code, $delimiter) { if (""==$code) return ""; if (!is_array($delimiter)) $delimiter = array( $delimiter => true ); $code = trim($code); $len = strlen($code); $enclosed = false; $enclosed_by = ""; if ( isset($delimiter[$code[0]]) ) { $i = 1; } else { for ($i=0; $i<$len; $i++) { $char = $code[$i]; if (('"'==$char || "'"==$char) && ($char == $enclosed_by || ""==$enclosed_by) && (0==$i || ($i>0 && "\\"!=$code[$i-1]))) { if (!$enclosed) $enclosed_by = $char; else $enclosed_by = ""; $enclosed = !$enclosed; } if (!$enclosed && isset($delimiter[$char])) break; } } return substr($code, 0, $i); } // end func getValue /** * Analyses a code snipped and returns the type and value of the first variable found. * * With version 0.3 PHPDoc tries to analyse variable declarations to find * type and value. This is used to analyse class variable declarations and * optional function arguments. * * Note that all regular expressions in this function start with "^". That means * you have to do some preparations to the code snippet you're passing to this * function. * * @param string PHP code to analyse * @param boolean Flag indicating the "type" of code to analyse. Optional * function parameters and class variables have a slightly * different syntax for arrays. By default function parameters are expected. * @return array $vartype $vartype[0] = type, $vartype[1] = value, $vartype[2] = raw value * @see */ function getVariableTypeAndValue($code, $flag_args = true) { $type = "unknown"; $value = "unknown"; $raw_value = $code; // // Do not change the order the function tries to find out the type. // if (preg_match( $this->PHP_COMPLEX["type_boolean"], $code, $regs)) { $type = "boolean"; $raw_value = $regs[0]; $value = $regs[0]; } else if (preg_match( $this->PHP_COMPLEX["type_string_enclosed"], $code, $regs)) { $type = "string"; $raw_value = $regs[0]; $value = $regs[0]; } else if (preg_match( $this->PHP_COMPLEX["type_int_oct"], $code, $regs)) { $type = "integer (octal)"; $raw_value = $regs[0]; $value = preg_replace("@\s@", "", $regs[0]); if ( (int)$value != $value ) $type.= " [warning: out of integer range, possible overflow trouble]"; $value = octdec($value)." ($value)"; } else if (preg_match( $this->PHP_COMPLEX["type_int_hex"], $code, $regs)) { $type = "integer (hexadecimal)"; $raw_value = $regs[0]; $value = preg_replace("@\s@", "", $regs[0]); if ( (int)$value != $value ) $type.= " [warning: out of integer range, possible overflow trouble]"; $value = hexdec($value)." ($value)"; } else if (preg_match( $this->PHP_COMPLEX["type_float_exponent"], $code, $regs)) { $type = "float"; $raw_value = $regs[0]; $value = (string)preg_replace("@\s@", "", $regs[0]); if ( (float)$value != $value ) $type.= " [warning: out of float range]"; $value = (float)$value; } else if (preg_match( $this->PHP_COMPLEX["type_float"], $code, $regs)) { $type = "float"; $raw_value = $regs[0]; $value = preg_replace("@\s@", "", $regs[0]); if ( (float)$value != $value ) $type.= " [warning: out of float range]"; $value = (float)$value; } else if (preg_match( $this->PHP_COMPLEX["type_number"], $code, $regs)) { $value = preg_replace("@\s@", "", $regs[0]); $raw_value = $regs[0]; if ( (int)$value == $value ) { $type = "integer"; $value = (int)$value; } else { $type = "float"; if ( (float)$value != $value ) $type.=" [warning: out of float range]"; $value = (float)$value; } } else if ($flag_args && preg_match( $this->PHP_COMPLEX["type_empty_array"], $code, $regs)) { $value = "array()"; $raw_value = $regs[0]; $type = "array"; } else if (!$flag_args && preg_match( $this->PHP_COMPLEX["type_array"], $code, $regs)) { $value = $this->getValue( $code, array(";" => true)); // strpos() is twice as fast as substr() if ( 0 == strpos($value, "array")) $type = "array"; $raw_value == $value; } else if (preg_match( $this->PHP_COMPLEX["type_string"], $code, $regs)) { $type = "string"; $raw_value = $regs[0]; $value = $regs[0]; } return array($type, $value, $raw_value); } // end func getVariableTypeAndValue } // end class PhpdocParserObject ?> Index: php4/pear/PHPDoc/parser/PhpdocParserRegExp.php +++ php4/pear/PHPDoc/parser/PhpdocParserRegExp.php <?php /** * Defines all regular expressions. * * This class defines all regular expressions. To make the * configuration and customization of PHPDoc as simple as * possible I decided to define all regular expressions in one class. * From a programming point of view there's no need to do so. * * @author Ulf Wendel <ulf@redsys.de> * @version 0.1 */ class PhpdocParserRegExp extends PhpdocObject { /** * Array of phpdoc tags, indexed by the tagname. * * ... grepping information is really not a parser. Don't * change the order the tags are listed. If you introduce * new tags write the long variant of the tagname (parameter) * in front of the shortcut (param). * * @var array List of all PHPDoc documentation tags. */ var $PHPDOC_TAGS = array( "@parameter" => '@param[eter] (object objectname|type) [$varname] [description]', "@param" => '@param[eter] (object objectname|type) [$varname] [description]', "@return" => '@return (object objectname|type) [$varname] [description]', "@access" => '@access', "@abstract" => '@abstract', "@static" => '@static', "@final" => '@final', "@throws" => '@throws exception [, exception]', "@see" => '@see (function()|$varname|(module|class)(function()|$varname)) [, (funtion()|$varname|(module|class)(function()|$varname))]', "@link" => '@link URL [description]', "@var" => '@var (object objectname|type) [$varname]', "@global" => '@global (object objectname|type) $varname [description]', "@constant" => '@const[ant] label [description]', "@const" => '@const[ant] label [description]', "@author" => '@author Name [<email>] [, Name [<email>]', "@copyright" => '@copyright description', "@version" => '@version label', "@since" => '@since label', "@deprecated" => '@deprec[ated] description', "@deprec" => '@deprec[ated] description', "@brother" => '@(brother|sister) (function()|$varname)', "@sister" => '@(brother|sister) (function()|$varname)', "@include" => '@include description', "@exclude" => '@exclude label', "@modulegroup" => '@modulegroup label', "@module" => '@module label', "@package" => '@package label', "@magic" => '@magic description', "@todo" => '@todo description' ); /** * Basis regular expressions used to compose complex expressions to grep doc comments. * * PHPDoc tries to compose all complex regular expressions * from a list of basic ones. This array contains all expressions * used grep complex doc comments and the surrounding keywords. * * @var array List of basic regular expressions matching parts of doc comments: * module names, module separator, vartypes, accesstypes. * @final * @see buildComplexRegExps(), $C_COMPLEX */ var $C_BASE = array( #"block" => '/\*\*((?:(?!\*).)*(?:\n(?!\s*\*/)\s*\*(?:(?!\*/).)*)*)\*/', "module" => "[^\s]+", "module_separator" => "::", "module_tags" => "(@modulegroup|@module)", "vartype" => "(string|integer|int|long|real|double|float|boolean|bool|mixed|array|object)", "access" => "(private|public)" ); /** * List of regular expressions used to grep complex doc comments. * * As with $PHP_COMPLEX all complex expressions are build using basic * ones in buildComplexRegExps(). * * @var array Regular expressions matching @see and optional objectnames. * @final * @see buildComplexRegexps(), $C_BASE */ var $C_COMPLEX = array( "objectname_optional" => "", "see_var" => "", "see_function" => "", "see_moduleclass" => "", "module_doc" => "", "module_tags" => "", "module_separator" => "", "module_separator_len" => 0, "module_separator_len_neg" => 0 ); /** * Basic RegExps used to analyse PHP Code. * * PHPDoc tries to compose all complex regular expressions * from some basic expressions. This array contains * all expressions used to build $PHP_COMPLEX. * There're some differences to the RegExps in zend-scanner.l, * e.g. I decided to write "\s+" instead of "[ \n\r\t]+" which * should be identical as long as perl compatible regular * expressions are used. Another point is that I did not break * down numbers to LNUM/DNUM. * * @var array List of basis regular expressions matching php code elements: * spaces, optional spaces, linebreaks, labels, use (include and friends), * optional argument assignment, boolean, several variable types. * @final * @see $PHP_COMPLEX */ var $PHP_BASE = array ( "space" => "\s+", "space_optional" => "\s*", "break" => "[\n\r]", "php_open_long" => "<\?php\s", # zend_scanner.l use {WHITESPACE} (space in our case) eighter. Might be slightly faster. "php_open_short" => "<\?", "php_open_asp" => "<%", "php_open_short_print" => "<\?=", "php_open_asp_print" => "<%=", # do not change the single quotes to double ones "label" => '[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\xzf-\xff]*', "use" => "(include_once|include|require_once|require)", "assignment" => "\s*([,=])\s*", "boolean" => "(true|false)", "string" => "[^\s]+", "string_enclosed" => "(['\"])(?:\\\\\\1|[^\\1])*?\\1", "int_oct" => "[+-]?\s*0[0-7]+", "int_hex" => "[+-]?\s*0[xX][0-9A-Fa-f]+", "float" => "[+-]?\s*\d*\.\d+", "float_exponent" => "[+-]?\s*\d*(?:\.\d+)*[eE][+-]?\d+", "number" => "[+-]?\s*\d+", "array" => "array\s*\(", "empty_array" => "array\s*\(\s*\)\s*" ); /** * List of regular expressions used to grep complex php code elements. * * The RegExp of the variable types is slightly changed to that * one in $PHP_BASE, getVariableTypeAndValue() needs this. * "undoc_*" is used to grep all keywords those who have a doc * comment in front and those without. See getPhodocParagraphs() * for more details on this. * * @var array RegExps to match: variablenames, functionnames, classnames, * class variable declarations, function declarations, * class declarations, defines, uses (include and friends), * function arguments, several variables types. * @see buildComplexRegExps(), getVariableTypeAndValue(), getPhpdocParagraphs(), $PHP_BASE */ var $PHP_COMPLEX = array ( "varname" => "", "functionname" => "", "classname" => "", "php_open_script" => "", "var" => "", "undoc_var" => "", "function" => "", "undoc_function" => "", "class" => "", "undoc_class" => "", "class_extends" => "", "undoc_class_extends" => "", "const" => "", "undoc_const" => "", "use" => "", "undoc_use" => "", "argument" => "", "type_boolean" => "", "type_string" => "", "type_string_enclosed" => "", "type_int_oct" => "", "type_int_hex" => "", "type_float" => "", "type_float_exponent" => "", "type_number" => "", "type_array" => "", "type_empty_array" => "" ); /** * Array of RegExp matching the syntax of several complex tags. * * The array is filled by the constructor. * * @var array Used to analyse return, var, param, * global, see and to find tags in general * @see PhpdocParserObject() */ var $TAGS = array ( "return" => "", "var" => "", # @var, @param "global" => "", "access" => "", "module" => "", # @module, @modulegroup "const" => "", # @const, @constant "see_var" => "", # @see "see_function" => "", # @see "see_class" => "", # @see "see_module" => "", # @see "link" => "@([^\s]+)(.*)@is", # @link "brother" => "", "author" => "<\s*([a-z]([-a-z0-9_.])*@([-a-z0-9_]*\.)+[a-z]{2,})\s*>", # @author <email> part "all" => "" # list of all known tags ); /** * Builds complex regular expressions for the parser. * * PHPDoc has a small set of basic regular expressions. All complex * regular expressions are made out of the basic ones. The composition * in done in this method. Note: every derived class must * call this method in it's constructor! * @see $PHP_BASE, $PHP_COMPLEX, $C_BASE, $C_COMPLEX */ function buildComplexRegExps() { // // Do not change the order of the variable initializations there're dependencies. // It starts with some php names. // // some names $this->PHP_COMPLEX["varname"] = sprintf("[&]?[$]%s", $this->PHP_BASE["label"] ); $this->PHP_COMPLEX["functionname"] = sprintf("[&]?%s", $this->PHP_BASE["label"] ); $this->PHP_COMPLEX["classname"] = $this->PHP_BASE["label"]; // // Now build all regexps used to grep doc comment elements. // // optional object name $this->C_COMPLEX["objectname_optional"] = sprintf("(?:object%s%s)?", $this->PHP_BASE["space"], $this->PHP_COMPLEX["classname"] ); $this->C_COMPLEX["module_separator"] = sprintf("(?:%s)", $this->C_BASE["module_separator"]); $this->C_COMPLEX["module_separator_len"] = strlen($this->C_BASE["module_separator"]); $this->C_COMPLEX["module_separator_len_neg"] = -1*strlen($this->C_BASE["module_separator"]); // References to other elements $this->C_COMPLEX["see_var"] = sprintf("(%s%s)?([$][^:]%s)", $this->C_BASE["module"], $this->C_COMPLEX["module_separator"], $this->PHP_BASE["label"] ); $this->C_COMPLEX["see_function"] = sprintf("(%s%s)?([^:]%s\(%s\))", $this->C_BASE["module"], $this->C_COMPLEX["module_separator"], $this->PHP_BASE["label"], $this->PHP_BASE["space_optional"] ); $this->C_COMPLEX["see_moduleclass"] = sprintf("(%s)", $this->C_BASE["module"] ); // // RegExps used to grep certain php code elements. // // var statements $this->PHP_COMPLEX["var"] = sprintf("|^%svar%s([$]%s)%s(=?)|is", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space"], $this->PHP_BASE["label"], $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"] ); $this->PHP_COMPLEX["undoc_var"] = sprintf("|%s|isS", substr($this->PHP_COMPLEX["var"], 2, -3) ); // function statements $this->PHP_COMPLEX["function"] = sprintf("|^%sfunction%s(%s)%s\(|is", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space"], $this->PHP_COMPLEX["functionname"], $this->PHP_BASE["space_optional"] ); $this->PHP_COMPLEX["undoc_function"] = sprintf("|%s|isS", substr($this->PHP_COMPLEX["function"], 2, -3) ); // class statements $this->PHP_COMPLEX["class"] = sprintf("|^%sclass%s(%s)%s{|is", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space"], $this->PHP_COMPLEX["classname"], $this->PHP_BASE["space_optional"] ); $this->PHP_COMPLEX["undoc_class"] = sprintf("|%s|isS", substr($this->PHP_COMPLEX["class"], 2, -3) ); $this->PHP_COMPLEX["class_extends"] = sprintf("|^%sclass%s(%s)%sextends%s(%s)%s{|is", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space"], $this->PHP_COMPLEX["classname"], $this->PHP_BASE["space"], $this->PHP_BASE["space"], $this->PHP_COMPLEX["classname"], $this->PHP_BASE["space_optional"] ); $this->PHP_COMPLEX["undoc_class_extends"] = sprintf("|%s|isS", substr($this->PHP_COMPLEX["class_extends"], 2, -3) ); // // RegExp used to grep define statements. // NOTE: the backticks do not allow the usage of $this->PHP_BASE // $this->PHP_COMPLEX["const"] = sprintf("@^%sdefine%s\(%s(%s)%s,%s(%s)%s(?:,%s(%s))?%s\)%s;@is", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"], "[$]?\w[\w-_]*|(['\"])(?:\\\\\\2|[^\\2])*?\\2", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"], "(['\"])(?:\\\\\\4|[^\\4])*?\\4|(?:true|false)|[+-]?\s*0[0-7]+|[+-]?\s*0[xX][0-9A-Fa-f]+|[+-]?\s*\d*(?:\.\d+)*[eE][+-]?\d+|[+-]?\s*\d*\.\d+|[+-]?\s*\d+|&?[$]?\w[\w-_]*", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"], "(?:true|false)|[+-]?\s*0[0-7]+|[+-]?\s*0[xX][0-9A-Fa-f]+|[+-]?\s*\d*(?:\.\d+)*[eE][+-]?\d+|[+-]?\s*\d*\.\d+|[+-]?\s*\d+|&?[$]?\w[\w-_]*|(['])(?:\\\\\\6|[^\\6])*?\\6", $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"] ); $this->PHP_COMPLEX["undoc_const"] = sprintf("@%s@isS", substr($this->PHP_COMPLEX["const"], 2, -3) ); // // include, include_once, require, require_once and friends // // ? removed! $this->PHP_COMPLEX["use"] = sprintf("@^%s%s[\(]%s((['\"])((?:\\\\\\3|[^\\3])*?)\\3|([^\s]+))%s[\)]%s;@is", $this->PHP_BASE["use"], $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"] ); $this->PHP_COMPLEX["undoc_use"] = sprintf("@%s@isS", substr($this->PHP_COMPLEX["use"], 2, -3) ); // // Variable name with an optional assignment operator. This one is used // to analyse function heads [parameter lists] as well as class variable // declarations. // $this->PHP_COMPLEX["argument"] = sprintf("|(%s)(%s)?|s", $this->PHP_COMPLEX["varname"], $this->PHP_BASE["assignment"] ); // // <script language="php"> syntax // $this->PHP_COMPLEX["php_open_script"] = sprintf("<script%slanguage%s=%s[\"']php[\"']%s>", $this->PHP_BASE["space"], $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"], $this->PHP_BASE["space_optional"] ); $this->PHP_COMPLEX["php_open_all"] = sprintf("(?:%s|%s|%s|%s|%s|%s)", $this->PHP_BASE["php_open_long"], $this->PHP_BASE["php_open_short"], $this->PHP_BASE["php_open_asp"], $this->PHP_BASE["php_open_short_print"], $this->PHP_BASE["php_open_asp_print"], $this->PHP_COMPLEX["php_open_script"] ); $this->C_COMPLEX["module_doc"] = sprintf("@^%s%s%s/\*\*@is", $this->PHP_BASE["space_optional"], $this->PHP_COMPLEX["php_open_all"], $this->PHP_BASE["space_optional"] ); $this->C_COMPLEX["module_tags"] = sprintf("/%s/is", $this->C_BASE["module_tags"] ); // // RegExp used to grep variables types // $elements = array( "boolean", "string", "string_enclosed", "int_oct", "int_hex", "float", "float_exponent", "number", "array", "empty_array" ); reset($elements); while (list($key, $name)=each($elements)) $this->PHP_COMPLEX["type_".$name] = sprintf("@^%s@", $this->PHP_BASE[$name]); // // Regular expressions used to analyse phpdoc tags. // $this->TAGS["return"] = sprintf("/%s%s(%s)%s(%s)?%s(.*)/is", $this->C_BASE["vartype"], $this->PHP_BASE["space_optional"], $this->C_COMPLEX["objectname_optional"], $this->PHP_BASE["space"], $this->PHP_COMPLEX["varname"], $this->PHP_BASE["space_optional"] ); $this->TAGS["var"] = sprintf("/%s%s(%s)%s(%s)?%s(.*)/is", $this->C_BASE["vartype"], $this->PHP_BASE["space_optional"], $this->C_COMPLEX["objectname_optional"], $this->PHP_BASE["space"], $this->PHP_COMPLEX["varname"], $this->PHP_BASE["space_optional"] ); $this->TAGS["global"] = sprintf("/%s%s(%s)%s(%s)%s(.*)/is", $this->C_BASE["vartype"], $this->PHP_BASE["space_optional"], $this->C_COMPLEX["objectname_optional"], $this->PHP_BASE["space"], $this->PHP_COMPLEX["varname"], $this->PHP_BASE["space_optional"] ); $this->TAGS["brother"] = sprintf("/(%s\(\)|\$%s)/is", $this->PHP_BASE["label"], $this->PHP_BASE["label"] ); $this->TAGS["const"] = sprintf("/(%s)%s(.*)/is", $this->PHP_BASE["label"], $this->PHP_BASE["space"] ); $this->TAGS["access"] = sprintf("/%s/is", $this->C_BASE["access"]); $this->TAGS["module"] = sprintf("/%s/is", $this->PHP_BASE["label"]); $this->TAGS["author"] = sprintf("/%s/is", $this->TAGS["author"]); $all_tags = ""; reset($this->PHPDOC_TAGS); while (list($tag, $v)=each($this->PHPDOC_TAGS)) $all_tags.= substr($tag, 1)."|"; $all_tags = substr($all_tags, 0, -1); $this->TAGS["all"] = "/@($all_tags)/is"; $elements = array ( "see_function", "see_var", "see_moduleclass" ); reset($elements); while (list($k, $index)=each($elements)) $this->TAGS[$index] = sprintf("/%s/is", $this->C_COMPLEX[$index]); } // end func buildComplexRegExps } // end class PhpdocParserRegExp ?> Index: php4/pear/PHPDoc/parser/PhpdocParserTags.php +++ php4/pear/PHPDoc/parser/PhpdocParserTags.php <?php class PhpdocParserTags extends PhpdocParserRegExp { /** * Extract the value from the given tags and copy the data to the $data array if its an allowed tag * @param array $tags array of tags returned by getTags * @param array $data array where the allowed tags and their values are copied to * @param array $allowed array of allowed (recognized) tags * @return array $data * @throws PhpdocError * @see getTags(), analyseVariableParagraphs(), analyseFunctionParagraphs(), analyseClassParagraphs(), analyseSeeTags() */ function analyseTags($tags, $data, $allowed) { if (!is_array($tags) || !is_array($data) || !is_array($allowed)) { $this->err[] = new PhpdocError("Illegal function call", __FILE__, __LINE__); return $data; } reset($tags); while (list($k, $tag)=each($tags)) { $tagname = substr( strtolower($tag["tag"]), 1 ); if (!isset($allowed[$tagname])) { $data["notallowed"][$tagname] = true; continue; } switch ($tagname) { # @tagname description case "exclude": case "package": case "magic": case "todo": case "version": case "since": case "include": case "copyright": if (isset($data[$tagname])) { $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used."; $data["syntaxerror"][] = $tag; break; } if (""==$tag["value"]) { $tag["msg"] = "Description is missing, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; $data[$tagname] = $tag["value"]; } else $data[$tagname] = $tag["value"]; break; # @tagname [void] case "abstract": case "static": case "final": if (isset($data[$tagname])) { $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used."; $data["syntaxerror"][] = $tag; break; } if (""!=$tag["value"]) { $tag["msg"] = "Description gets ignored, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } $data[$tagname] = true; break; case "var": case "return": if (isset($data[$tagname])) { $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used."; $data["syntaxerror"][] = $tag; break; } if (preg_match($this->TAGS[$tagname], $tag["value"], $regs)) { if ("object"==$regs[1]) { $type = "object "; if ( ""==$regs[2] ) { $type.= "[unknown]"; $tag["msg"] = "Objectname is missing, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } else { $type.= $regs[2]; } } else { $type = $regs[1]; } $data[$tagname] = array ( "type" => $type, "name" => $regs[3] ); if ("return"==$tagname && ""!=$regs[4]) $data[$tagname]["desc"] = $regs[4]; } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "global": if (preg_match($this->TAGS["global"], $tag["value"], $regs)) { if (""==$regs[3]) { $tag["msg"] = "Variablename ist missing, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } if ("object"==$regs[1]) { $type = "object "; if ( ""==$regs[2] ) { $type.= "[unknown]"; $tag["msg"] = "Objectname is missing, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } else { $type.= $regs[2]; } } else { $type = $regs[1]; } if ("" == $regs[4]) { $data["global"][] = array ( "type" => $type, "name" => $regs[3] ); } else { $data["global"][] = array ( "type" => $type, "name" => $regs[3], "desc" => $regs[4] ); } } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "param": case "parameter": if (preg_match($this->TAGS["var"], $tag["value"], $regs)) { if ("object"==$regs[1]) { $type = "object "; if ( ""==$regs[2] ) { $type.= "[unknown]"; $tag["msg"] = "Objectname is missing, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } else { $type.= $regs[2]; } } else { $type = $regs[1]; } if (""==$regs[4]) { $data["params"][] = array ( "type" => $type, "name" => $regs[3] ); } else { $data["params"][] = array ( "type" => $type, "name" => $regs[3], "desc" => $regs[4] ); } } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "see": if (""!=$tag["value"]) { $error = ""; $references = explode(",", $tag["value"] ); reset($references); while (list($k, $reference)=each($references)) { if (preg_match($this->TAGS["see_var"], $reference, $regs)) { list($msg, $entry) = $this->analyseSeeTagRegs($regs); $error.=$msg; if (count($entry)>0) $data["see"]["var"][] = $entry; } else if (preg_match($this->TAGS["see_function"], $reference, $regs)) { list($msg, $entry) = $this->analyseSeeTagRegs($regs); $error.=$msg; if (count($entry)>0) $data["see"]["function"][] = $entry; } else if (preg_match($this->TAGS["see_moduleclass"], $reference, $regs)) { $name = $regs[1]; if (substr($name, 0, $this->C_COMPLEX["module_separator_len"]) == $this->C_BASE["module_separator"]) { $name = substr($name, $this->C_COMPLEX["module_separator_len"]); if (""==$name) { $error.= "Element name is missing: '$regs[0]'. Reference gets ignored"; continue; } else { $error.= "Element name starts with moduleseparator, module name forgotten '$regs[0]'?"; } } $data["see"]["moduleclass"][] = $name; } else { $error.= "Unknown syntax '$reference'"; } } if (""!=$error) { $tag["msg"] = sprintf("Could not understand all references. %s. Syntax: '%s' (function), '%s' (variable), '%s' (module or class).", $error, $this->C_COMPLEX["see_function"], $this->C_COMPLEX["see_var"], $this->C_COMPLEX["see_moduleclass"] ); $data["syntaxerror"][] = $tag; } } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "link": if (preg_match($this->TAGS["link"], $tag["value"], $regs)) { $desc = trim($regs[2]); if ("" == $desc) { $data["link"][] = array( "url" => $regs[1] ); } else { $data["link"][] = array( "url" => $regs[1], "desc" => trim($regs[2]) ); } } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "throws": if (""!=$tag["value"]) { $exceptions = explode(",", $tag["value"]); reset($exceptions); while (list($k, $exception)=each($exceptions)) $data["throws"][] = trim($exception); } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "access": if (preg_match($this->TAGS["access"], $tag["value"], $regs)) { $data["access"] = $regs[1]; } else { $tag["msg"] = (""==$tag["value"]) ? "General syntax error," : "Access modifier unknown,"; $tag["msg"].= " '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "deprec": case "deprecated": if (isset($data["deprec"])) { $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used."; $data["syntaxerror"][] = $tag; break; } if (""!=$tag["value"]) { $data["deprec"] = $tag["value"]; } else { $tag["msg"] = "Description is missing, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "brother": case "sister": if (isset($data["brother"])) { $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used."; $data["syntaxerror"][] = $tag; break; } if (""!=$tag["value"]) { if (preg_match($this->TAGS["brother"], $tag["value"], $regs)) { $data["brother"] = $regs[1]; } else { $tag["msg"] = "Can't find a function name nor a variable name, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } } else { $data["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "module": case "modulegroup": if (isset($data[$tagname])) { $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used."; $data["syntaxerror"][] = $tag; break; } if (""!=$tag["value"]) { if (preg_match($this->TAGS["module"], $tag["value"], $regs)) { $data[$tagname] = $regs[0]; } else { $tag["msg"] = "Illegal label used, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "const": case "constant": if (isset($data["const"])) { $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used."; $data["syntaxerror"][] = $tag; break; } if (""!=$tag["value"]) { if (preg_match($this->TAGS["const"], $tag["value"], $regs)) { $data["const"] = array( "name" => $regs[1], "desc" => trim($regs[2]) ); } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } } else { $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; case "author": if (""!=$tag["value"]) { $authors = explode(",", $tag["value"]); reset($authors); while (list($k, $author)=each($authors)) { if (preg_match($this->TAGS["author"], $author, $regs)) { $data["author"][] = array( "name" => trim(substr($author, 0, strpos($author, $regs[0]))), "mail" => trim($regs[1]) ); } else if (""!=trim($author)) { $data["author"][] = array( "name" => trim($author) ); } else { $tag["msg"] = "Name is missing in enumeration, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } } } else { $tag["msg"] = "General syntax error, syntax: ".$this->PHPDOC_TAGS["@$tagname"]."'."; $data["syntaxerror"][] = $tag; } break; default: // I'm quite sure this default is obsolete, but I don't feel like checking it. // Anyway this array index should get used one fine day. $data["unknown"][] = $tag; break; } } return $data; } // end func analyseTags /** * Helperfunction to analyse @see tags * @param array Array return by preg_match() * @return array $see[0] = error, $see[1] = data * @see analyseTags() */ function analyseSeeTagRegs($regs) { $error = ""; $group = $regs[1]; $name = $regs[2]; if (substr($name, 0, $this->C_COMPLEX["module_separator_len"]) == $this->C_BASE["module_separator"]) { $name = substr($name, $this->C_COMPLEX["module_separator_len"]); if (""==$name) { $error = "Element name is missing '$regs[0]'. Reference gets ignored"; return array($error, array()); } else { $error = "Element name starts with moduleseparator, module name forgotten '$regs[0]'?"; } } if (""!=$group && $this->C_BASE["module_separator"]!=$group) { $group = substr($group, 0, $this->C_COMPLEX["module_separator_len_neg"]); if (""==$group) { $error = "Groupname missing '$regs[0]'."; $data = array( "name" => $name ); } else { $data = array ( "group" => $group, "name" => $name ); } } else { $data = array ( "name" => $name ); } return array($error, $data); } // end func analyseSeeTagRegs /** * Extracts PHPDoc tags from a PHPDoc doc comment. * @param string Doc comment. * @return array List of tags ordered by their appearance containing the * tag name and it's (unparsed) value. * @see getTagPos() */ function getTags($phpdoc) { $positions = $this->getTagPos($phpdoc); if (0==count($positions)) return array(); reset($positions); list($k, $data)=each($positions); $lastpos = $data["pos"]; $lasttag = $data["tag"]; while (list($k, $data)=each($positions)) { $line = substr($phpdoc, $lastpos, ($data["pos"]-$lastpos)); $value = trim(substr($line, strlen($lasttag))); $tags[] = array ("tag" => $lasttag, "value" => $value ); $lastpos = $data["pos"]; $lasttag = $data["tag"]; } $line = substr($phpdoc, $lastpos); $value = trim(substr($line, strlen($lasttag))); $tags[] = array ("tag" => $lasttag, "value" => $value ); return $tags; } // end func getTags /** * Find the position of the next phpdoc tag. * @param string $phpdoc * @param int $offset * @return array $t 0 => tag, 1 => offset * @access private * @see findTags() */ function getTagPos($phpdoc, $offset=0) { $positions = array(); preg_match_all($this->TAGS["all"], $phpdoc, $regs, PREG_SET_ORDER); reset($regs); while (list($k, $data)=each($regs)) { $pos = strpos($phpdoc, $data[0], $offset); if ($pos>0 || $data[0]==substr($phpdoc, 0, strlen($data[0])) ) { $positions[] = array ("pos" => $pos, "tag" => $data[0]); $offset = $pos+1; } } return $positions; } // end func getTagPos /** * Takes an array filled by analyseTags() and converts the parse errors into a single error message. * * Checks for [syntaxerror], [notallowed] and [unknown] entries in the data hash, * converts them into an error message and unsets the indizes. The function * returns a hash containing the aggregates error message and the modified * data hash. * * @param array $data * @param string $mode Keyword where the data hash comes from eg. function/class... * @return array array( $error_msg, $data ) */ function checkParserErrors($data, $mode) { $msg = ""; // tags with an incorrect syntax if (isset($data["syntaxerror"])) { $msg.= "PHPDoc found ".count($data["syntaxerror"])." syntax error(s) in the tag list. "; reset($data["syntaxerror"]); while (list($k, $error)=each($data["syntaxerror"])) $msg.= sprintf("Tag: '%s %s' - %s.", $error["tag"], $error["value"], $error["msg"]); unset($data["syntaxerror"]); } // tags that are not allowed in this context if (isset($data["notallowed"])) { $msg.= count($data["notallowed"])." tag[s] were used that are not allowed in $mode doc comments: "; reset($data["notallowed"]); while (list($tagname, $v)=each($data["notallowed"])) $msg.= "$tagname, "; $msg = substr($msg, 0, -2)."."; unset($data["notallowed"]); } // unknown tags if (isset($data["unknown"])) { $msg.= "PHPDoc found ".count($data["unknown"])." tag[s] that are unknown: "; reset($data["unknown"]); while (list($k, $error)=each($data["unknown"])) $msg.= sprintf("%s, ", $error["tag"]); $msg = substr($msg, 0, -2)."."; unset($data["unknown"]); } return array($msg, $data); } // end func checkParserErrors } // end class PhpdocParserTags ?> Index: php4/pear/PHPDoc/parser/PhpdocUseParser.php +++ php4/pear/PHPDoc/parser/PhpdocUseParser.php <?php /** * Extracts use statements (include and friends) an thheir documentation from php code. * @author Ulf Wendel <ulf.wendel@redsys.de> * @version 0.1alpha */ class PhpdocUseParser extends PhpdocParserCore { /** * Structure of an empty use entry. * @var array */ var $emptyUse = array( "type" => "", "file" => "", "undoc" => true ); /** * List of allowed tags in use doc comments. * @var array */ var $useTags = array( "return" => true, "see" => true, "link" => true, "authhor" => true, "copyright" => true, "version" => true, "since" => true, "deprecated" => true, "deprec" => true, "include" => true, "exclude" => true, "magic" => true, "todo" => true ); /** * Takes the result from getPhpdocParagraphs() and interprets it. * @param array */ function analyseUse($para) { $use = $this->emptyUse; $use["file"] = $para["file"]; if (""!=$para["doc"]) { $use = $this->analyseTags($this->getTags($para["doc"]), $use, $this->useTags); list($msg, $use) = $this->checkParserErrors($use, "use (include and friends)"); if (""!=$msg) $this->warn->addDocWarning($this->currentFile, "use", $use["file"], $msg, "mismatch"); list($use["sdesc"], $use["desc"]) = $this->getDescription($para["doc"]); $use["undoc"] = false; } $use["type"] = $para["type"]; return $use; } // end func analyseUse } // end class PhpdocUseParser ?> Index: php4/pear/PHPDoc/parser/PhpdocVariableParser.php +++ php4/pear/PHPDoc/parser/PhpdocVariableParser.php <?php /** * Extract class variables and their documentation from phpcode * @author Ulf Wendel <ulf@redsys.de> * @version 0.2 */ class PhpdocVariableParser extends PhpdocModuleParser { /** * Array with default values of a variable * * @var array $emptyVariable * @see variables, analyseVariableParagraph() */ var $emptyVariable = array( "name" => "", "undoc" => true ); /** * Array of tags that are allowed in front of the var keyword * @var array $variableTags * @see analyseVariableParagraph() */ var $variableTags = array( "access" => true, "abstract" => true, "static" => true, "final" => true, "see" => true, "link" => true, "var" => true, "version" => true, "since" => true, "deprecated" => true, "deprec" => true, "brother" => true, "sister" => true, "exclude" => true, "magic" => true, "todo" => true ); /** * Analyses a variable doc comment * @param array Hash returned by getPhpdocParagraph() * @return array */ function analyseVariable($para) { $variable = $this->emptyVariable; $variable["name"] = $para["name"]; if (""!=$para["doc"]) { $variable = $this->analyseTags($this->getTags($para["doc"]), $variable, $this->variableTags); list($msg, $variable) = $this->checkParserErrors($variable, "variable"); if (""!=$msg) $this->warn->addDocWarning($this->currentFile, "variable", $variable["name"], $msg, "mismatch"); list($variable["sdesc"], $variable["desc"]) = $this->getDescription($para["doc"]); $variable["undoc"] = false; } list($type, $value, $raw_value) = $this->getVariableTypeAndValue($para["value"], false); $variable["type"] = $type; $variable["value"] = $value; $variable = $this->checkVarDocs($variable); return $variable; } // end func analyseVariables /** * Compares the @var tag informations with the analyse of the source code. * * @param array $variable * @return array $variable */ function checkVarDocs($variable) { if (!isset($variable["var"])) return $variable; if ("unknown"!=$variable["type"] && $variable["var"]["type"] != $variable["type"]) { $msg = sprintf("The documented class variable type does not match the type found. Update the tag to '@var %s [%s]'.", $variable["type"], $variable["name"] ); $this->warn->addDocWarning($this->currentFile, "variable", $variable["name"], $msg, "mismatch"); } if ($variable["var"]["name"] != $variable["name"]) { $msg = sprintf("The documented class variable name does not match the name found. Update the tag to '@var %s [%s]'.", $variable["type"], $variable["name"] ); $this->warn->addDocWarning($this->currentFile, "variable", $variable["name"], $msg, "mismatch"); } unset($variable["var"]); return $variable; } // end func checkVarDocs } // end class PhpdocVariableParser ?> Index: php4/pear/PHPDoc/redist/IT.php +++ php4/pear/PHPDoc/redist/IT.php <?php /** * Integrated Template - IT * * Well there's not much to say about it. I needed a template class that * supports a single template file with multiple (nested) blocks inside. * * Usage: * $tpl = new IntegratedTemplate( [string filerootdir] ); * * // load a template or set it with setTemplate() * $tpl->loadTemplatefile( string filename [, boolean removeUnknownVariables, boolean removeEmptyBlocks] ) * * // set "global" Variables meaning variables not beeing within a (inner) block * $tpl->setVariable( string variablename, mixed value ); * * // like with the Isotopp Templates there's a second way to use setVariable() * $tpl->setVariable( array ( string varname => mixed value ) ); * * // Let's use any block, even a deeply nested one * $tpl->setCurrentBlock( string blockname ); * * // repeat this as often as you neer. * $tpl->setVariable( array ( string varname => mixed value ) ); * $tpl->parseCurrentBlock(); * * // get the parsed template or print it: $tpl->show() * $tpl->get(); * * @author Ulf Wendel <uw@netuse.de> * @version 1.1 28/09/00 * @access public */ class IntegratedTemplate extends PhpdocObject { /** * Contains the error objects * @var array * @access public * @see halt(), $printError, $haltOnError */ var $err = array(); /** * Print error messages? * @var boolean * @access public * @see halt(), $haltOnError, $err */ var $printError = false; /** * Call die() on error? * @var boolean * @access public * @see halt(), $printError, $err */ var $haltOnError = false; /** * Clear cache on get()? * @var boolean */ var $clearCache = false; /** * First character of a variable placeholder ( _{_VARIABLE} ). * @var string * @access public * @see $closingDelimiter, $blocknameRegExp, $variablenameRegExp */ var $openingDelimiter = "{"; /** * Last character of a variable placeholder ( {VARIABLE_}_ ). * @var string * @access public * @see $openingDelimiter, $blocknameRegExp, $variablenameRegExp */ var $closingDelimiter = "}"; /** * RegExp matching a block in the template. * Per default "sm" is used as the regexp modifier, "i" is missing. * That means a case sensitive search is done. * @var string * @access public * @see $variablenameRegExp, $openingDelimiter, $closingDelimiter */ var $blocknameRegExp = "[0-9A-Za-z_-]+"; /** * RegExp matching a variable placeholder in the template. * Per default "sm" is used as the regexp modifier, "i" is missing. * That means a case sensitive search is done. * @var string * @access public * @see $blocknameRegExp, $openingDelimiter, $closingDelimiter */ var $variablenameRegExp = "[0-9A-Za-z_-]+"; /** * Full RegExp used to find variable placeholder, filled by the constructor. * @var string Looks somewhat like @(delimiter varname delimiter)@ * @see IntegratedTemplate() */ var $variablesRegExp = ""; /** * Full RegExp used to find blocks an their content, filled by the constructor. * @var string * @see IntegratedTemplate() */ var $blockRegExp = ""; /** * Name of the current block. * @var string */ var $currentBlock = "__global__"; /** * Content of the template. * @var string */ var $template = ""; /** * Array of all blocks and their content. * * @var array * @see findBlocks() */ var $blocklist = array(); /** * Array with the parsed content of a block. * * @var array */ var $blockdata = array(); /** * Array of variables in a block. * @var array */ var $blockvariables = array(); /** * Array of inner blocks of a block. * @var array */ var $blockinner = array(); /** * Future versions will use this... * @var array */ var $blocktypes = array(); /** * Variable cache. * * Variables get cached before any replacement is done. * Advantage: empty blocks can be removed automatically. * Disadvantage: might take some more memory * * @var array * @see setVariable(), $clearCacheOnParse */ var $variableCache = array(); /** * @var boolean */ var $clearCacheOnParse = true; /** * Controls the handling of unknown variables, default is remove. * @var boolean */ var $removeUnknownVariables = true; /** * Controls the handling of empty blocks, default is remove. * @var boolean */ var $removeEmptyBlocks = true; /** * Root directory for all file operations. * The string gets prefixed to all filenames given. * @var string * @see IntegratedTemplate(), setRoot() */ var $fileRoot = ""; /** * Internal flag indicating that a blockname was used multiple times. * @var boolean */ var $flagBlocktrouble = false; /** * Flag indicating that the global block was parsed. * @var boolean */ var $flagGlobalParsed = false; var $debug = false; /** * Builds some complex regular expressions and optinally sets the file root directory. * * Make sure that you call this constructor if you derive your template * class from this one. * * @param string File root directory, prefix for all filenames given to the object. * @see setRoot() */ function IntegratedTemplate($root = "") { $this->variablesRegExp = "@".$this->openingDelimiter."(".$this->variablenameRegExp.")".$this->closingDelimiter."@sm"; $this->blockRegExp = '@!--\s+BEGIN\s+('.$this->blocknameRegExp.')\s+-->(.*)<!--\s+END\s+\1\s+-->@sm'; $this->setRoot($root); } // end constructor /** * Print a certain block with all replacements done. * @brother get() */ function show($block = "__global__") { print $this->get($block); } // end func show /** * Returns a block with all replacements done. * * @param string name of the block * @return string * @access public * @see show() */ function get($block = "__global__") { if ("__global__" == $block && !$this->flagGlobalParsed) $this->parse("__global__"); if (!isset($this->blocklist[$block])) { $this->halt("The block '$block' was not found in the template.", __FILE__, __LINE__); return true; } if ($this->clearCache) { $data = (isset($this->blockdata[$block])) ? $this->blockdata[$block] : ""; unset($this->blockdata[$block]); return $data; } else { return (isset($this->blockdata[$block])) ? $this->blockdata[$block] : ""; } } // end func get() /** * Parses the given block. * * @param string name of the block to be parsed * @access public * @see parseCurrentBlock() */ function parse($block = "__global__", $flag_recursion = false) { if (!isset($this->blocklist[$block])) { $this->halt("The block '$block' was not found in the template.", __FILE__, __LINE__); return false; } if ("__global__" == $block) $this->flagGlobalParsed = true; $regs = array(); $values = array(); if ($this->clearCacheOnParse) { reset($this->variableCache); while (list($name, $value)=each($this->variableCache)) { $regs[] = "@".$this->openingDelimiter.$name.$this->closingDelimiter."@"; $values[] = $value; } $this->variableCache = array(); } else { reset($this->blockvariables[$block]); while (list($k, $allowedvar)=each($this->blockvariables[$block])) { if (isset($this->variableCache[$allowedvar])) { $regs[] = "@".$this->openingDelimiter.$allowedvar.$this->closingDelimiter."@"; $values[] = $this->variableCache[$allowedvar]; unset($this->variableCache[$allowedvar]); } } } $outer = preg_replace($regs, $values, $this->blocklist[$block]); $empty = (0==count($values)) ? true : false; if (isset($this->blockinner[$block])) { reset($this->blockinner[$block]); while (list($k, $innerblock)=each($this->blockinner[$block])) { $this->parse($innerblock, true); if (""!=$this->blockdata[$innerblock]) { if ($this->debug) print "<tr><td colspan=2>NICHT LEER: $innerblock</td></tr>"; $empty = false; } $placeholder = $this->openingDelimiter."__".$innerblock."__".$this->closingDelimiter; $outer = str_replace($placeholder, $this->blockdata[$innerblock], $outer); $this->blockdata[$innerblock] = ""; } } if ($this->removeUnknownVariables) $outer = preg_replace($this->variablesRegExp, "", $outer); if ($this->debug) { ?> <tr> <td colspan=2>Block: <?php echo $block; ?></td> </tr> <tr><td>Empty?</td><td><?php $this->j($empty); ?></td></tr> <tr><td>removeEmptyBlocks?</td><td><?php $this->j($this->removeEmptyBlocks); ?></td></tr> <tr><td>Rekursion?</td><td><?php $this->j($flag_recursion); ?></td></tr> <tr><td>Values</td><td><?php print count($values); ?></td></tr> <?php } if ($empty) { if (!$this->removeEmptyBlocks) $this->blockdata[$block].= $outer; } else { $this->blockdata[$block].= $outer; } return $empty; } // end func parse function j($bool) { if ($bool) print "ja"; else print "nein"; } /** * Parses the current block * @see parse(), setCurrentBlock(), $currentBlock * @access public */ function parseCurrentBlock() { return $this->parse($this->currentBlock); } // end func parseCurrentBlock /** * Sets a variable value. * * The function can be used eighter like setVariable( "varname", "value") * or with one array $variables["varname"] = "value" given setVariable($variables) * quite like phplib templates set_var(). * * @param mixed string with the variable name or an array %variables["varname"] = "value" * @param string value of the variable or empty if $variable is an array. * @param string prefix for variable names * @access public */ function setVariable($variable, $value="") { if (is_array($variable)) { reset($variable); while (list($var, $value)=each($variable)) $this->variableCache[$var] = $value; } else { $this->variableCache[$variable] = $value; } } // end func setVariable /** * Sets the name of the current block that is the block where variables are added. * * @param string name of the block * @return boolean false on failure otherwise true * @access public */ function setCurrentBlock($block = "__global__") { if (!isset($this->blocklist[$block])) { $this->halt("Can't find the block '$block' in the template.", __FILE__, __LINE__); return false; } $this->currentBlock = $block; return true; } // end func setCurrentBlock /** * Clears all datafields of the object and rebuild the internal blocklist * * LoadTemplatefile() and setTemplate() automatically call this function * when a new template is given. Don't use this function * unless you know what you're doing. * * @access public * @see free() */ function init() { $this->free(); $this->findBlocks($this->template); $this->buildBlockvariablelist(); } // end func init /** * Clears all datafields of the object. * * Don't use this function unless you know what you're doing. * * @access public * @see init() */ function free() { $this->err[] = ""; $this->currentBlock = "__global__"; $this->variableCache = array(); $this->blocklist = array(); $this->blockvariables = array(); $this->blockinner = array(); $this->blockdata = array(); $this->blocklookup = array(); $this->blocktypes = array(); $this->flagBlocktrouble = false; $this->flagGlobalParsed = false; } // end func free /** * Sets the template. * * You can eighter load a template file from disk with LoadTemplatefile() or set the * template manually using this function. * * @param string template content * @param boolean Unbekannte, nicht ersetzte Platzhalter entfernen? * @param boolean remove unknown/unused variables? * @param boolean remove empty blocks? * @see LoadTemplatefile(), $template * @access public */ function setTemplate($template, $removeUnknownVariables = true, $removeEmptyBlocks = false) { if (""==$template) { $this->halt("The given string is empty.", __FILE__, __LINE__); return false; } $this->removeUnknownVariables = $removeUnknownVariables; $this->removeEmptyBlocks = $removeEmptyBlocks; $this->template = '<!-- BEGIN __global__ -->'.$template.'<!-- END __global__ -->'; $this->init(); if ($this->flagBlocktrouble) return false; return true; } // end func setTemplate /** * Reads a template file from the disk. * * @param string name of the template file, full path! * @param boolean remove unknown/unused variables? * @param boolean remove empty blocks? * @access public * @return boolean false on failure, otherwise true * @see $template, setTemplate() */ function loadTemplatefile($filename, $removeUnknownVariables = true, $removeEmptyBlocks = true) { $template = $this->getfile($filename); return $this->setTemplate($this->getFile($filename), $removeUnknownVariables, $removeEmptyBlocks); } // end func LoadTemplatefile /** * Sets the file root. The file root gets prefixed to all filenames passed to the object. * * Make sure that you override this function when using the class * on windows. * * @param string * @see IntegratedTemplate() * @access public */ function setRoot($root) { if (""!=$root && "/"!= substr($root, -1)) $root.="/"; $this->fileRoot = $root; } // end func setRoot /** * Build a list of all variables within a block */ function buildBlockvariablelist() { reset($this->blocklist); while (list($name, $content)=each($this->blocklist)) { preg_match_all( $this->variablesRegExp, $content, $regs ); $this->blockvariables[$name] = $regs[1]; } } // end func buildBlockvariablelist /** * Returns a list of all */ function getGlobalvariables() { $regs = array(); $values = array(); reset($this->blockvariables["__global__"]); while (list($k, $allowedvar)=each($this->blockvariables["__global__"])) { if (isset($this->variableCache[$allowedvar])) { $regs[] = "@".$this->openingDelimiter.$allowedvar.$this->closingDelimiter."@"; $values[] = $this->variableCache[$allowedvar]; unset($this->variableCache[$allowedvar]); } } return array($regs, $values); } // end func getGlobalvariables /** * Recusively builds a list of all blocks within the template. * * @param string string that gets scanned * @access private * @see $blocklist */ function findBlocks($string) { $blocklist = array(); if (preg_match_all($this->blockRegExp, $string, $regs, PREG_SET_ORDER)) { reset($regs); while (list($k, $match)=each($regs)) { $blockname = $match[1]; $blockcontent = $match[2]; if (isset($this->blocklist[$blockname])) { $this->halt("The name of a block must be unique within a template. Found '$blockname' twice. Unpredictable results may appear.", __FILE__, __LINE__); $this->flagBlocktrouble = true; } $this->blocklist[$blockname] = $blockcontent; $this->blockdata[$blockname] = ""; $blocklist[] = $blockname; $inner = $this->findBlocks($blockcontent); reset($inner); while (list($k, $name)=each($inner)) { $pattern = sprintf('@<!--\s+BEGIN\s+%s\s+-->(.*)<!--\s+END\s+%s\s+-->@sm', $name, $name ); $this->blocklist[$blockname] = preg_replace( $pattern, $this->openingDelimiter."__".$name."__".$this->closingDelimiter, $this->blocklist[$blockname] ); $this->blockinner[$blockname][] = $name; $this->blockparents[$name] = $blockname; } } } return $blocklist; } // end func findBlocks /** * Reads a file from disk and returns its content. * @param string Filename * @return string Filecontent */ function getFile($filename) { if ("/" == substr($filename, 0, 1)) $filename = substr($filename, 1); $filename = $this->fileRoot.$filename; if ( !($fh = @fopen($filename, "r")) ) { $this->halt("Can't read '$filename'.", __FILE__, __LINE__); return ""; } $content = fread($fh, filesize($filename)); fclose($fh); return $content; } // end func getFile /** * Error Handling function. * @param string error message * @param mixed File where the error occured * @param int Line where the error occured * @see $err */ function halt($message, $file="", $line=0) { $message = sprintf("IntegratedTemplate Error: %s [File: %s, Line: %d]", $message, $file, $line ); $this->err[] = $message; if ($this->printError) print $message; if ($this->haltOnError) die($message); } // end func halt } // end class IntegratedTemplate ?> Index: php4/pear/PHPDoc/redist/ITX.php +++ php4/pear/PHPDoc/redist/ITX.php <?php /** * Integrated Template Extension - ITX * * With this class you get the full power of the phplib template class. * You may have one file with blocks in it but you have as well one main file * and multiple files one for each block. This is quite usefull when you have * user configurable websites. Using blocks not in the main template allows * you to some parts of your layout easily. * * Note that you can replace an existing block and add new blocks add runtime. * Adding new blocks means changing a variable placeholder to a block. * * @author Ulf Wendel <uw@netuse.de> * @access public * @version 1.0 22/09/00 */ class IntegratedTemplateExtension extends IntegratedTemplate { /** * Array with all warnings. * @var array * @access public * @see $printWarning, $haltOnWarning, warning() */ var $warn = array(); /** * Print warnings? * @var array * @access public * @see $haltOnWarning, $warn, warning() */ var $printWarning = false; /** * Call die() on warning? * @var boolean * @access public * @see $warn, $printWarning, warning() */ var $haltOnWarning = false; /** * RegExp used to test for a valid blockname. * @var string */ var $checkblocknameRegExp = ""; /** * Builds some complex regexps and calls the constructor of the parent class. * * Make sure that you call this constructor if you derive you own * template class from this one. * * @see IntegratedTemplate() */ function IntegratedTemplateExtension() { $this->checkblocknameRegExp = "@".$this->blocknameRegExp."@"; $this->IntegratedTemplate(); } // end func IntegratedTemplateExtension /** * Replaces an existing block with new content. Warning: not implemented yet. * * The Replacement does not affect previously added variables. All data is cached. * In case the new block does contain less or other variable placeholder the previously * passed data that is no longer referenced will be deleted. The internal list * of allowed variables gets updated as well. * * In case the original block contains other blocks it must eighter have placeholder * for the inner blocks or contain them. If you want to use placeholder the placeholder must * look like openingDelimiter."__".blockname."__".closingDelimiter . * * Due to the cache updates replaceBlock() and replaceBlockfile() are "expensive" operations * which means extensive usage will slow down your script. So try to avoid them and if * you can't do so try to use them before you pass lots of variables to the block you're * replacing. * * @param string Blockname * @param string Blockcontent * @return boolean * @see replaceBlockfile(), addBlock(), addBlockfile() * @access public */ function replaceBlock($block, $template) { if (!isset($this->blocklist[$block])) { $this->halt("The block '$block' does not exist in the template and thus it can't be replaced.", __FILE__, __LINE__); return false; } if (""==$template) { $this->halt("No block content given.", __FILE__, __LINE__); return false; } print "This function has not been coded yet."; // find inner blocks // add to variablelist // compare variable list // update caches return true; } // end func replaceBlock /** * Replaces an existing block with new content from a file. Warning: not implemented yet. * @brother replaceBlock() * @param string Blockname * @param string Name of the file that contains the blockcontent */ function replaceBlockfile($block, $filename) { return $this->replaceBlock($block, $this->getFile($filename)); } // end func replaceBlockfile /** * Adds a block to the template changing a variable placeholder to a block placeholder. * * Add means "replace a variable placeholder by a new block". * This is different to PHPLibs templates. The function loads a * block, creates a handle for it and assigns it to a certain * variable placeholder. To to the same with PHPLibs templates you would * call set_file() to create the handle and parse() to assign the * parsed block to a variable. By this PHPLibs templates assume that you tend * to assign a block to more than one one placeholder. To assign a parsed block * to more than only the placeholder you specify in this function you have * to use a combination of getBlock() and setVariable(). * * As no updates to cached data is necessary addBlock() and addBlockfile() * are rather "cheap" meaning quick operations. * * The block content must not start with <!-- BEGIN blockname --> and end with * <!-- END blockname --> this would cause overhead and produce an error. * * @param string Name of the variable placeholder, the name must be unique within the template. * @param string Name of the block to be added * @param string Content of the block * @return boolean * @see addBlockfile() * @access public */ function addBlock($placeholder, $blockname, $template) { // Don't trust any user even if it's a programmer or yourself... if (""==$placeholder) { $this->halt("No variable placeholder given.", __FILE__, __LINE__); return false; } else if (""==$blockname || !preg_match($this->checkblocknameRegExp, $blockname) ) { print $this->checkblocknameRegExp; $this->halt("No or invalid blockname '$blockname' given.", __FILE__, __LINE__); return false; } else if (""==$template) { $this->halt("No block content given.", __FILE__, __LINE__); return false; } else if (isset($this->blocklist[$blockname])) { $this->halt("The block already exists.", __FILE__, __LINE__); return false; } // Hmm, we should do some more tests. $parents = $this->findPlaceholderBlocks($placeholder); if (0==count($parents)) { $this->halt("The variable placeholder '$placeholder' was not found in the template.", __FILE__, __LINE__); return false; } else if ( count($parents)>1 ) { reset($parents); while (list($k, $parent)=each($parents)) $msg.= "$parent, "; $msg = substr($parent, -2); $this->halt("The variable placeholder '$placeholder' must be unique, found in multiple blocks '$msg'.", __FILE__, __LINE__); return false; } $template = "<!-- BEGIN $blockname -->".$template."<!-- END $blockname -->"; $this->findBlocks($template); if ($this->flagBlocktrouble) return false; // findBlocks() already throws an exception $this->blockinner[$parents[0]][] = $blockname; $this->blocklist[$parents[0]] = preg_replace( "@".$this->openingDelimiter.$placeholder.$this->closingDelimiter."@", $this->openingDelimiter."__".$blockname."__".$this->closingDelimiter, $this->blocklist[$parents[0]] ); $this->deleteFromBlockvariablelist($parents[0], $placeholder); $this->updateBlockvariablelist($blockname); return true; } // end func addBlock /** * Adds a block taken from a file to the template changing a variable placeholder to a block placeholder. * * @param string Name of the variable placeholder to be converted * @param string Name of the block to be added * @param string File that contains the block * @brother addBlock() */ function addBlockfile($placeholder, $blockname, $filename) { return $this->addBlock($placeholder, $blockname, $this->getFile($filename)); } // end func addBlockfile /** * Deletes one or many variables from the block variable list. * @param string Blockname * @param mixed Name of one variable or array of variables ( array ( name => true ) ) to be stripped. */ function deleteFromBlockvariablelist($block, $variables) { if (!is_array($variables)) $variables = array( $variables => true); reset($this->blockvariables[$block]); while (list($k, $varname)=each($this->blockvariables[$block])) if (isset($variables[$varname])) unset($this->blockvariables[$block][$k]); } // end deleteFromBlockvariablelist /** * Updates the variable list of a block. * @param string Blockname */ function updateBlockvariablelist($block) { preg_match_all( $this->variablesRegExp, $this->blocklist[$block], $regs ); $this->blockvariables[$block] = $regs[1]; } // end func updateBlockvariablelist /** * Returns an array of blocknames where the given variable placeholder is used. * @param string Variable placeholder * @return array $parents parents[0..n] = blockname */ function findPlaceholderBlocks($variable) { $parents = array(); reset($this->blocklist); while (list($blockname, $content)=each($this->blocklist)) { reset($this->blockvariables[$blockname]); while (list($k, $varname)=each($this->blockvariables[$blockname])) if ($variable == $varname) $parents[] = $blockname; } return $parents; } // end func findPlaceholderBlocks /** * Handles warnings, saves them to $warn and prints them or calls die() depending on the flags * @param string Warning * @param string File where the warning occured * @param int Linenumber where thr warning occured * @see $warn, $printWarning, $haltOnWarning */ function warning($message, $file="", $line=0) { $message = sprintf("IntegratedTemplateExtension Warning: %s [File: %s, Line: %d]", $message, $file, $line ); $this->warn[] = $message; if ($this->printWarning) print $message; if ($this->haltOnError) die($message); } // end func warning } // end class IntegratedTemplateExtension ?> Index: php4/pear/PHPDoc/renderer/PhpdocRendererObject.php +++ php4/pear/PHPDoc/renderer/PhpdocRendererObject.php <?php class PhpdocRendererObject extends PhpdocObject { var $warn; var $accessor; } // end class PhpdocRendererObject ?> Index: php4/pear/PHPDoc/warning/PhpdocWarning.php +++ php4/pear/PHPDoc/warning/PhpdocWarning.php <?php /** * Container for all kind of Warnings the parser/analyser recognizes * * The base of the report generator module is this container. It's currently * pretty simple and will change later on... */ class PhpdocWarning extends PhpdocObject { /** * Hash of documentation failures. * @var array */ var $doc_warnings = array(); /** * Counter containing the number of documentation warnings. * @var int * @see getNumDocWarnings(), getNumWarnings() */ var $num_doc_warnings = 0; /** * Adds a warning to the list of class documentation failures. * @param string Name of the file that containts the error * @param string Kind of the element that caused the error: module, class, function, variable, use, const * @param string Name of the class/function/... that caused the warning * @param string Warning message itself * @para, string Type of the error: missing, mismatch, syntax, ... * @access public * @see addDocWarning() */ function addDocWarning($file, $elementtype, $elementname, $warning, $type="missing") { $this->doc_warnings[$file][$elementtype][] = array( "name" => $elementname, "type" => $type, "msg" => $warning ); $this->num_doc_warnings++; } // end func addDocWarning /** * Returns a list of warnings. * * @return array $warnings * @access public */ function getWarnings() { return $this->doc_warnings; } // end func getParserWarnings /** * Returns the total number of documentation warnings. * @access public * @see getNumParserWarnings(), getNumWarnings() */ function getNumDocWarnings() { return $this->num_doc_warnings; } // end func getNumDocWarnings } // end class PhpdocWarning ?> Index: php4/pear/PHPDoc/xmlexporter/PhpdocXMLClassExporter.php +++ php4/pear/PHPDoc/xmlexporter/PhpdocXMLClassExporter.php <?php /** * Exports the data of a class as an xml file. * * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version 1.0 */ class PhpdocXMLClassExporter extends PhpdocXMLDocumentExporter { function PhpdocXMLClassExporter() { $this->PHPDocXMLExporter(); } // end constructor /** * Variable container attributes. * @var array $variableAttriutes */ var $variableAttributes = array( "name" => "CDATA", "access" => "CDATA", "type" => "CDATA", "abstract" => "Boolean", "static" => "Boolean", "final" => "Boolean" ); /** * Class container attributes. * @var array $classAttributes */ var $classAttributes = array( "name" => "CDATA", "extends" => "CDATA", "undoc" => "Boolean", "access" => "CDATA", "abstract" => "Boolean", "static" => "Boolean", "final" => "Boolean", "package" => "CDATA" ); var $fileprefix = "class_"; function create() { $attribs = $this->getAttributes($this->result, $this->classAttributes); $this->xmlwriter->startElement("class", "", $attribs, false); $this->filenameXML($this->result["filename"]); $this->docXML($this->result); if (isset($this->result["functions"])) $this->functionsXML($this->result["functions"]); if (isset($this->result["variables"])) $this->variablesXML($this->result["variables"]); if (isset($this->result["uses"])) $this->usesXML($this->result["uses"]); if (isset($this->result["consts"])) $this->constsXML($this->result["consts"]); if (isset($this->result["inherited"])) $this->inheritedOverridenXML($this->result["inherited"], "inherited"); if (isset($this->result["overriden"])) $this->inheritedOverridenXML($this->result["overriden"], "overriden"); if (isset($this->result["path"])) $this->pathXML($this->result["path"]); if (isset($this->result["baseclass"])) $this->baseclassXML($this->result["baseclass"]); if (isset($this->result["subclasses"])) $this->subclassesXML($this->result["subclasses"]); $this->xmlwriter->endElement("class", true); } // end func create /** * Handles inherited and overriden elements. * * @param array Array of inherited or overriden elements * @param string Container used when saving the elements */ function inheritedOverridenXML($data, $tag) { reset($data); while (list($type, $elements)=each($data)) { reset($elements); while (list($from, $data2)=each($elements)) { $attribs = $this->getAttributes( array ("type" => $type, "src" => $from), $this->inheritedOverridenAttributes); $this->xmlwriter->startElement($tag, "", $attribs, false); reset($data2); while (list($name, $v)=each($data2)) $this->xmlwriter->addElement("element", $name); $this->xmlwriter->endElement($tag, true); } } } // end func inheritedOverridenXML /** * Writes the "path" (inheritance chain) of an element. * @param array */ function pathXML($path) { if (0==count($path)) return; $this->xmlwriter->startElement("path", "", "", false); reset($path); while (list($k, $parent)=each($path)) $this->xmlwriter->addElement("parent", $parent); $this->xmlwriter->endElement("path", true); } // end func pathXML /** * Adds a baseclass container to the generated xml. * @param string Name of the baseclass */ function baseclassXML($base) { if (""!=$base) $this->xmlwriter->addElement("baseclass", $base); } // end func baseclassXML /** * Adds a list of subclasses to the generated xml. * @param array */ function subclassesXML($subclasses) { if (0==count($subclasses)) return; $this->xmlwriter->startElement("subclasses", "", "", false, true); reset($subclasses); while(list($subclass, $v)=each($subclasses)) $this->xmlwriter->addElement("subclass", $subclass); $this->xmlwriter->endElement("subclasses", true); } // end func subclassesXML /** * Writes class variables to */ function variablesXML($variables) { reset($variables); while (list($variable, $data)=each($variables)) { $attribs = $this->getAttributes($data, $this->variableAttributes); $this->xmlwriter->startElement("variable", $data["value"], $attribs, false); $this->docXML($data); $this->xmlwriter->endElement("variable", true); } } // end func variablesXML } // end class PhpdocXMLClassExporter ?> Index: php4/pear/PHPDoc/xmlexporter/PhpdocXMLDocumentExporter.php +++ php4/pear/PHPDoc/xmlexporter/PhpdocXMLDocumentExporter.php <?php /** * Base of the class and module exporter. */ class PhpdocXMLDocumentExporter extends PhpdocXMLExporter { /** * Mapping from internal result array index name to xml tag name. * @var array $docTags */ var $docTags = array( "desc" => "description", "sdesc" => "shortdescription", "version" => "version", "since" => "since", "version" => "version", "deprec" => "deprecated", "copyright" => "copyright", "exclude" => "exclude", "brother" => "brother", "magic" => "magic" ); /** * Attributes of the <see> container. * @var array $seeAttributes */ var $seeAttributes = array( "type" => "CDATA", "group" => "CDATA" ); /** * Attributes of the <link> container. * @var array $linkAttributes */ var $linkAttributes = array( "url" => "CDATA" ); /** * Attributes of the <author> container. * @var array $authorAttributes */ var $authorAttributes = array( "email" => "CDATA" ); /** * Attributes of <inherited> and <overriden> container. * @var array $inheritedOverridenAttributes */ var $inheritedOverridenAttributes = array( "src" => "CDATA", "type" => "CDATA" ); /** * Attributes of the <constant> container. * @var array $constAttributes */ var $constAttributes = array( "name" => "CDATA", "undoc" => "Boolean", "access" => "CDATA", "case" => "CDATA" ); /** * Attribues of the <uses> container. * @var array $usesAttributes */ var $usesAttributes = array( "type" => "CDATA", "file" => "CDATA", "undoc" => "Boolean" ); /** * Attribues of the <function> container. * @var array $functionAttributes */ var $functionAttributes = array( "name" => "CDATA", "undoc" => "Boolean", "access" => "CDATA", "abstract" => "Boolean", "static" => "CDATA" ); /** * Attributes of the <return> container. * @var array $returnAttribues */ var $returnAttributes = array( "name" => "CDATA", "type" => "CDATA" ); /** * Attributes of the <global> container. * @var array $globalAttributes */ var $globalAttributes = array( "name" => "CDATA", "type" => "CDATA" ); /** * Attributes of the <param> container. * @var array $paramAttributes */ var $paramAttributes = array( "name" => "CDATA", "default" => "CDATA", "type" => "CDATA", "undoc" => "Boolean" ); /** * Writes a <file> container. * @param string $file filename */ function filenameXML($file) { $this->xmlwriter->addElement("file", $file); } // end func filenameXML /** * Adds all constants (define(), @const) to the xml document. * @param array Array of constants * @return boolean Returns false on failure otherwise true */ function constsXML($consts) { if (!is_array($consts) || 0==count($consts)) return true; reset($consts); while (list($k, $data)=each($consts)) { $attribs = $this->getAttributes($data, $this->constAttributes); $this->xmlwriter->startElement("constant", (isset($data["value"])) ? $data["value"] : "", $attribs, false, true); $this->docXML($data); $this->xmlwriter->endElement("constant", true); } return true; } // end func constsXML function usesXML($uses) { if (!is_array($uses)) { $this->err[] = new PhpdocError("No array given.", __FILE__, __LINE__); return false; } reset($uses); while (list($k, $data)=each($uses)) { $attribs = $this->getAttributes($data, $this->usesAttributes); $this->xmlwriter->startElement("uses", "", $attribs, false, true); $this->docXML($data); $this->xmlwriter->endElement("uses", true); } return true; } // end func usesXML function functionsXML($functions) { if (!is_array($functions)) { $this->err[] = new PhpdocError("No array given.", __FILE__, __LINE__); return false; } #$this->introspection("functions", $functions); reset($functions); while (list($k, $data)=each($functions)) { $attribs = $this->getAttributes($data, $this->functionAttributes); $this->xmlwriter->startElement("function", "", $attribs, false, true); $this->docXML($data); $this->xmlwriter->endElement("function", true); } } // end functionsXML function docXML($data) { $this->xmlwriter->startElement("doc", "", "", false, true); if (isset($data["link"])) $this->linkXML($data["link"]); if (isset($data["author"])) $this->authorXML($data["author"]); if (isset($data["see"])) $this->seeXML($data["see"]); if (isset($data["params"])) $this->paramsXML($data["params"]); if (isset($data["return"])) $this->returnXML($data["return"]); if (isset($data["throws"])) $this->throwsXML($data["throws"]); if (isset($data["global"])) $this->globalsXML($data["global"]); if (isset($data["inherited"])) { $attribs = array( "src" => array( "type" => $this->inheritedOverridenAttributes["src"], "value" => $data["inherited"] ) ); $this->xmlwriter->addElement("inherited", "", $attribs); } if (isset($data["overrides"])) { $attribs = array( "src" => array( "type" => $this->inheritedOverridenAttributes["src"], "value" => $data["overrides"] ) ); $this->xmlwriter->addElement("overriden", "", $attribs); } reset($this->docTags); while (list($field, $tag)=each($this->docTags)) if (isset($data[$field])) $this->xmlwriter->addElement($tag, $data[$field], ""); $this->xmlwriter->endElement("doc", true); } // end func docXML function globalsXML($globals) { reset($globals); while (list($k, $data)=each($globals)) { $attribs = $this->getAttributes($data, $this->globalAttributes); $this->xmlwriter->addElement("global", (isset($data["desc"])) ? $data["desc"] : "", $attribs); } } // end func globalsXML function throwsXML($exceptions) { reset($exceptions); while (list($k, $exception)=each($exceptions)) $this->xmlwriter->addElement("throws", $exception, "", true); } // end func throwsXML function returnXML($return) { $desc = ""; if (is_array($return)) { if (isset($return["desc"])) { $desc = $return["desc"]; unset($return["desc"]); } $attribs = $this->getAttributes($return, $this->returnAttributes); } else { $attribs["type"] = array( "type" => "CDATA", "value" => $return ); } $this->xmlwriter->addElement("return", $desc, $attribs); } // end func returnXML function paramsXML($params) { reset($params); while (list($k, $data)=each($params)) { $attribs = $this->getAttributes($data, $this->paramAttributes); $this->xmlwriter->addElement("parameter", (isset($data["desc"])) ? $data["desc"] : "", $attribs); } } // end func paramsXML function authorXML($authors) { reset($authors); while (list($k, $data)=each($authors)) { $attribs = array(); if (isset($data["mail"])) $attribs = array( "email" => array( "type" => $this->authorAttributes["email"], "value" => $data["mail"] ) ); $this->xmlwriter->addElement("author",$data["name"], $attribs); } } // end func authorXML function linkXML($links) { reset($links); while (list($k, $data)=each($links)) { $attribs = array( "url" => array( "type" => $this->linkAttributes["url"], "value" => $data["url"] ) ); $this->xmlwriter->addElement("link", (isset($data["desc"])) ? $data["desc"] : "", $attribs); } } // end func linkXML function seeXML($see) { reset($see); while (list($type, $data)=each($see)) { reset($data); while (list($k, $data2)=each($data)) { $attribs = array( "type" => array( "type" => $this->seeAttributes["type"], "value" => strtolower($type) ) ); if (isset($data2["group"])) $attribs["group"] = array( "type" => $this->seeAttributes["group"], "value" => $data2["group"] ); $this->xmlwriter->addElement("see", $data2["name"], $attribs); } } } // end func SeeXML } // end class PhpdocXMLDocumentExporter ?> Index: php4/pear/PHPDoc/xmlexporter/PhpdocXMLExporter.php +++ php4/pear/PHPDoc/xmlexporter/PhpdocXMLExporter.php <?php /** * Exporter used to export phpdoc internals data structures as xml documents. * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version 1.0 */ class PhpdocXMLExporter extends PhpdocObject { /** * Filename prefix for the generated xml document. * * This class variable must be overriden by all derived classes. * PHPDoc uses the filename prefix to detect the content of * the file. * * @var string $fileprefix */ var $fileprefix = ""; /** * Target directory where the xml documents get saved. * @var string $path * @see setPath() */ var $path = ""; /** * Data to save as a xml document. * @var array $result * @see setResult(), export() */ var $result = array(); /** * Instance of PhpdocXMLWriter used to generate the xml document. * @var object PhpdocXMLWriter * @see PhpdocXMLExporter() */ var $xmlwriter; /** * Creates a PhpdocXMLWriter object. * * Make sure that all derived classes call this constructor. * * @see $xmlwriter */ function PhpdocXMLExporter() { $this->xmlwriter = new PhpdocXMLWriter; } // end constructor /** * Sets the target path for the generated xml documents. * * @param string * @see $path * @access public */ function setPath($path) { $this->path = $path; } // end func setPath /** * Exports the given result array as xml document. * * @param array * @param string name of the target xml file * @access public * @see create(), $result */ function export($result, $xmlfile="") { if (0==count($result)) return; $this->result = $result; $this->xmlwriter->addXML('<?xml version="1.0"?>'); $this->xmlwriter->startElement("phpdoc", "", "", false, true); $this->create(); $this->xmlwriter->endElement("phpdoc", true); if (""==$xmlfile) $xmlfile = $this->result["name"]; /* if (file_exists($this->path.$xmlfile)) { $i = 1; while (file_exists($this->path.$name."_".$i.".xml")) $i++; $xmlfile = $name."_".$i.".xml"; } */ $xmlfile = $this->nameToURL($xmlfile); $xmlfile = $this->path.$this->fileprefix.$xmlfile.".xml"; $this->xmlwriter->export($xmlfile); $this->xmlwriter->free(); } // end func export function setResult($result) { $this->result = $result; $this->create(); } // end func setResult /** * Kind of array_intersect for xml attributes. * * This functions takes a data array and a list of allowed fields in the data * array. All of the allowed fields that exists in the data array will be * copied to returned array which looks like: * $attribs[name] = array ( type => allowed[name], value => data[name] ). * This structure is used by PhpdocXMLWriter->addElement(). * * @param array data array * @param array array of allowed fields and their attribute type * @return array $attribs */ function getAttributes($data, $allowed) { $attribs = array(); reset($allowed); while (list($tag, $type)=each($allowed)) if (isset($data[$tag])) $attribs[$tag] = array( "type" => $type, "value" => $data[$tag] ); return $attribs; } // end func getAttributes } // end PhpdocXMLExporter ?> Index: php4/pear/PHPDoc/xmlexporter/PhpdocXMLIndexExporter.php +++ php4/pear/PHPDoc/xmlexporter/PhpdocXMLIndexExporter.php <?php /** * Creates XML index files */ class PhpdocXMLIndexExporter extends PhpdocXMLExporter { /** * Chapter tag attributes * @var array */ var $chapterAttributes = array( "name" => "CDATA" ); /** * Element tag attributes. * @var array() */ var $elementAttributes = array( "type" => "CDATA", "source" => "CDATA", "sourcetype" => "CDATA" ); /** * Just call the parent class constructor */ function PhpdocXMLIndexExporter() { $this->PhpdocXMLExporter(); } // end constructor /** * Exports a modulegroup. * * @param array * @access public */ function exportModulegroup(&$modulegroup) { $this->xmlwriter->free(); $this->xmlwriter->addXML('<?xml version="1.0"?>'); $this->xmlwriter->startElement("phpdoc"); reset($modulegroup); list($group, $modules)=each($modulegroup); $attribs = array( "name" => array( "type" => "CDATA", "value" => $group) ); $this->xmlwriter->startElement("modulegroup", "", $attribs); reset($modules); while (list($k, $module)=each($modules)) $this->xmlwriter->addElement("module", "", array( "name" => array( "type" => "CDATA", "value" => $module )) ); $this->xmlwriter->endElement("modulegroup"); $this->xmlwriter->endElement("phpdoc"); $group = $this->nameToUrl($group); $this->xmlwriter->export($this->path."modulegroup_$group.xml"); $this->xmlwriter->free(); } // end func exportModulegroup /** * Exports a packagelist * * @param array * @access public */ function exportPackagelist(&$packagelist) { $this->xmlwriter->free(); $this->xmlwriter->addXML('<?xml version="1.0"?>'); $this->xmlwriter->startElement("phpdoc"); $this->xmlwriter->startElement("packagelist"); reset($packagelist); while (list($package, $elementlist)=each($packagelist)) { $attribs = array( "name" => array("type" => "CDATA", "value" => $package) ); $this->xmlwriter->startElement("package", "", $attribs); reset($elementlist); while (list($type, $elements)=each($elementlist)) { $container = ("classes"==$type) ? "class" : "module"; while (list($k, $element)=each($elements)) { $attribs = array( "name" => array("type" => "CDATA", "value" => $element)); $this->xmlwriter->addElement($container, "", $attribs); } } $this->xmlwriter->endElement("package"); } $this->xmlwriter->endElement("packagelist"); $this->xmlwriter->endElement("phpdoc"); $this->xmlwriter->export($this->path."packagelist.xml"); $this->xmlwriter->free(); } // end func exportPackagelist /** * Exports a classtree * * @param array * @access public */ function exportClasstree(&$classtree, $baseclass) { $this->xmlwriter->free(); $this->xmlwriter->addXML('<?xml version="1.0"?>'); $this->xmlwriter->startElement("phpdoc"); $attribs = array("baseclass" => array("type" => "CDATA", "value" => $baseclass)); $this->xmlwriter->startElement("classtree", "", $attribs); reset($classtree); while (list($parentclass, $subclasses)=each($classtree)) { $attribs = array("name" => array("type" => "CDATA", "value" => $parentclass)); $this->xmlwriter->startElement("class", "", $attribs); reset($subclasses); while (list($subclass, $v)=each($subclasses)) $this->xmlwriter->addElement("subclass", $subclass); $this->xmlwriter->endElement("class"); } $this->xmlwriter->endElement("classtree"); $this->xmlwriter->endElement("phpdoc"); $baseclass = $this->nameToURL($baseclass); $this->xmlwriter->export($this->path."classtree_$baseclass.xml"); $this->xmlwriter->free(); } // end func exportClasstree /** * Exports a list of all elements * * @param array * @access public */ function exportElementlist(&$elementlist) { $this->xmlwriter->free(); $this->xmlwriter->addXML('<?xml version="1.0"?>'); $this->xmlwriter->startElement("phpdoc"); $this->xmlwriter->startElement("index"); reset($elementlist); while (list($index, $elements)=each($elementlist)) { $attrib = array( "char" => array( "type" => "CDATA", "value" => $index ) ); $this->xmlwriter->startElement("chapter", "", $attrib); reset($elements); while (list($name, $element)=each($elements)) { reset($element); while (list($k, $eldata)=each($element)) { $attribs = $this->getAttributes($eldata, $this->elementAttributes); $attribs["name"] = array( "type" => "CDATA", "value" => $name ); $this->xmlwriter->addElement("element", $eldata["sdesc"], $attribs); } } $this->xmlwriter->endElement("chapter"); } $this->xmlwriter->endElement("index"); $this->xmlwriter->endElement("phpdoc"); $this->xmlwriter->export($this->path."elementlist.xml"); $this->xmlwriter->free(); } // end func exportElementlist } // end class PhpdocXMLIndexExporter ?> Index: php4/pear/PHPDoc/xmlexporter/PhpdocXMLModuleExporter.php +++ php4/pear/PHPDoc/xmlexporter/PhpdocXMLModuleExporter.php <?php /** * Exports the data of a module as an xml document * * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version 1.0 */ class PhpdocXMLModuleExporter extends PhpdocXMLDocumentExporter { /** * Module container attributes * @var array $moduleAttributes */ var $moduleAttributes = array( "name" => "CDATA", "group" => "CDATA", "undoc" => "Boolean", "access" => "CDATA", "package" => "CDATA" ); var $fileprefix = "module_"; function PhpdocXMLModuleExporter() { $this->PhpdocXMLExporter(); } // end constructor function create() { $attribs = $this->getAttributes($this->result, $this->moduleAttributes); $this->xmlwriter->startElement("module", "", $attribs); $this->filenameXML($this->result["filename"]); $this->docXML($this->result); if (isset($this->result["functions"])) $this->functionsXML($this->result["functions"]); if (isset($this->result["uses"])) $this->usesXML($this->result["uses"]); if (isset($this->result["consts"])) $this->constsXML($this->result["consts"]); $this->xmlwriter->endElement("module", true); } // end func create } // end class PhpdocXMLModuleExporter ?> Index: php4/pear/PHPDoc/xmlexporter/PhpdocXMLWarningExporter.php +++ php4/pear/PHPDoc/xmlexporter/PhpdocXMLWarningExporter.php <?php /** * Exports a list of documentation warnings found by phpdoc * */ class PhpdocXMLWarningExporter extends PhpdocXMLExporter { /** * Attributes of a warning container. * @var array */ var $warningAttributes = array( "name" => "CDATA", "type" => "CDATA", "elementtype" => "CDATA" ); var $fileprefix = "warnings_"; function PhpdocXMLWarningExporter() { $this->PhpdocXMLExporter(); } // end constructor function create() { reset($this->result); while (list($file, $warnings)=each($this->result)) { $this->xmlwriter->startElement("warnings", "", array("file" => array( "type" => "CDATA", "value" => $file))); reset($warnings); while (list($type, $warning)=each($warnings)) { reset($warning); while (list($k, $data)=each($warning)) { $data["elementtype"] = $type; $this->xmlwriter->addElement("warning", $data["msg"], $this->getAttributes($data, $this->warningAttributes)); } } $this->xmlwriter->endElement("warnings"); } } // end function create } // end class PhpdocXMLWarningExporter ?> Index: php4/pear/PHPDoc/xmlreader/PhpdocXMLReader.php +++ php4/pear/PHPDoc/xmlreader/PhpdocXMLReader.php <?php /** * Reads XML documents into a multi dimensional Array. * * @author Ulf Wendel <ulf.wendel@phpdoc.de> */ class PhpdocXMLReader extends PhpdocObject { /** * PHPDocFileHandler object. * @var object PhpdocFileHandler * @see createFileHandler() */ var $filehandler; /** * Values array from xml_parse_into_struct(). * @var array * @see parse(), stripCloseFromStructvalues(), importXML() */ var $structvalues = array(); /** * Parses a given XML file and returns the data as a hash. * * Please do not ask me for a in detail explanation of how it is done, * the documentation is in the source... * * @param string $filename Name of the xml document * @access public * @throws PhpdocError * @see importXML() */ function parse($filename) { if (""==$filename) { $this->err[] = new PhpdocError("No filename given.", __FILE__, __LINE__); return array(); } $parser = @xml_parser_create(); if (!$parser) { $this->err = PhpdocError("Can't create a XML Parser.", __FILE__, __LINE__); return array(); } xml_parser_set_option($parser, XML_OPTION_CASE_FOLDING, 0); $this->createFileHandler(); $xml = $this->filehandler->getFile($filename); $values = array(); $index = array(); xml_parse_into_struct($parser, $xml, &$values, &$index); xml_parser_free($parser); $this->structvalues = $values; $this->stripCloseFromStructvalues(); list($data, $last) = $this->importXML(); $this->structvalues = array(); return $data; } // end func parse /** * Creates a PhpdocFileHandler object and saves it to $filehandler if it does not already exist. * @see $filehandler */ function createFilehandler() { if (!isset($this->filehandler)) $this->filehandler = new PhpdocFileHandler; } // end func createFilehandler /** * Strips all values out of the xml_parse_intro_struct() values array with the type "open". * * @see $structvalues */ function stripCloseFromStructvalues() { $values = array(); reset($this->structvalues); while (list($k, $v)=each($this->structvalues)) if ("close"!=$v["type"]) $values[] = $v; $this->structvalues = $values; } // end func stripCloseFromStructvalues /** * Converts an xml_parse_into_struct value array to an array that's simmilar to phpdocs internal arrays. * * Well, don't ask me to explain this hack. Just take it as it. For those who want to unterstand and optimize * it: * - PHP3 compatibility is a must * - no XML DOM * - no eval(), this can't be optimized by the compiler * * @param int * @param int * @return array $data[0] = daten, $data[1] = some index value used for the recursion * @see addToArray() */ function importXML($start = 0, $allowed_level = 1) { $data = array(); $last = 0; for ($i=$start; $i<count($this->structvalues); $i++) { if ($allowed_level != $this->structvalues[$i]["level"]) break; $value = (isset($this->structvalues[$i]["value"])) ? $this->structvalues[$i]["value"] : ""; $attribs = (isset($this->structvalues[$i]["attributes"])) ? $this->structvalues[$i]["attributes"] : ""; $tag = $this->structvalues[$i]["tag"]; if ("open" == $this->structvalues[$i]["type"]) { list($inner, $next) = $this->importXML($i+1, $this->structvalues[$i]["level"]+1); // append the inner data to the current one $data = $this->addToArray($data, $tag, $value, $attribs, $inner); // skip some entries in $this->structvalues $i = $next; } else { // same level, append to the array $data = $this->addToArray($data, $tag, $value, $attribs); } // remember the last index in $this->structvalues we've worked on $last = $i; } return array($data, $last); } // end func importXML /** * Appends some values to an array * Well, don't ask me; just improve it with the remarks on buildXMLResult() * @param array * @param string * @param string * @param array * @param array * @return array $target */ function addToArray($target, $key, $value="", $attributes = "", $inner="") { if (!isset($target[$key]["value"]) && !isset($target[$key][0])) { if (""!=$inner) $target[$key] = $inner; if (""!=$attributes) { reset($attributes); while (list($k, $v)=each($attributes)) $target[$key][$k] = $this->xmldecode($v); } $target[$key]["value"] = $this->xmldecode($value); } else { if (!isset($target[$key][0])) { $oldvalue = $target[$key]; $target[$key] = array(); $target[$key][0] = $oldvalue; if (""!=$inner) $target[$key][1] = $inner; if (""!=$attributes) { reset($attributes); while (list($k, $v)=each($attributes)) $target[$key][1][$k] = $this->xmldecode($v); } $target[$key][1]["value"] = $this->xmldecode($value); } else { $index = count($target[$key]); if (""!=$inner) $target[$key][$index] = $inner; if (""!=$attributes) { reset($attributes); while (list($k, $v)=each($attributes)) $target[$key][$index][$k] = $this->xmldecode($v); } $target[$key][$index]["value"] = $this->xmldecode($value); } } return $target; } // end func addToArray function xmldecode($value) { #return preg_replace( array("@&lt;@", "@&gt;@", "@&apos;@", "@&quot;@", "@&amp;@"), array("<", ">", "'", '"', "&"), $value); return utf8_decode(preg_replace( array("@&lt;@", "@&gt;@", "@&apos;@", "@&quot;@", "@&amp;@"), array("<", ">", "'", '"', "&"), $value)); } // end func xmldecode } // end class PhpdocXMLReader ?> Index: php4/pear/PHPDoc/xmlwriter/PhpdocXMLWriter.php +++ php4/pear/PHPDoc/xmlwriter/PhpdocXMLWriter.php <?php /** * Creates XML documents. * * PHPDoc uses this helper class to generate xml documents. It's * not much what this class can do but it provides some simple * functions to handle attributes and hides file handling tasks. * * @author Ulf Wendel <ulf.wendel@phpdoc.de> * @version 1.0 */ class PhpdocXMLWriter extends PhpdocObject { /** * Generated XML document. * * @var string $xml */ var $xml = ""; /** * PHPDoc Warning object * * @var object PhpdocWarning */ var $warn; /** * Filehandler used for IO operations * * @var object PhpdocFilehandler * @see PhpdocXMLWriter */ var $fileHandler; /** * Creates a new PhpdocFileHandler * * @see $filehandler */ function PhpdocXMLWriter() { $this->fileHandler = new PhpdocFileHandler; } // end constructor /** * Clears the internal xml data buffer so that a new document can be passed to the object. * @access public */ function free() { $this->xml = ""; } // end func free /** * Adds xml to the generated xml. * * @param string xml to append * @access public */ function addXML($xml) { $this->xml.= $xml; } // end func addXML /** * Saves the xml to the specified file. * * @param string Name of the target file * @access public */ function export($filename) { return $this->fileHandler->createFile($filename, $this->xml); } // end func export /** * Adds an open (or single) xml tag to the generated xml. * * Use this function to add new elements/tags to the xml document. * The tagname and all attributenames will be converted to lowercase. * * @param string elementname (tagname) * @param string value of the container: <name>value * @param array Array of attributes: $attribs[n][type] = boolean|cdata, $attribs[n][value] = value * @param boolean Flag indication that you want an empty tag like <name/>. * @access public * @see endElement() */ function startElement($name, $value="", $attribs="", $close = false) { $xml = "<".strtolower($name); if (is_array($attribs)) { reset($attribs); while (list($attrib, $data)=each($attribs)) { $attrib = strtolower($attrib); $type = strtolower($data["type"]); switch($type) { case "boolean": $xml.= sprintf(' %s="%s"', $attrib, ($data["value"]) ? "true" : "false"); break; case "cdata": $xml.= sprintf(' %s="%s"', $attrib, $this->xmlencode($data["value"]) ); break; } } } if ($close) { $xml.= "/>"; } else { $xml.= ">"; if (""!=$value) $xml.= $this->xmlencode($value); } $this->xml.= $xml; } // end func startElement /** * Adds a closing xml tag to the generated xml document. * * @param string Elementname (tagname) * @access public * @see startElement() */ function endElement($name) { $this->xml.= sprintf("</%s>", strtolower($name) ); } // end func endElement /** * Adds a complete xml container to the generated xml document. * * @param string Elementname (tagname) * @param string Value * @param array Attributes * @access public * @see startElement(), endElement() */ function addElement($name, $value="", $attribs="") { if (""==$value) { $this->startElement($name, $value, $attribs, true); } else { $this->startElement($name, $value, $attribs, false); $this->endElement($name); } } // end func addElement /** * Encodes XML values. * @param string $value * @return string $value */ function xmlencode($value) { # return preg_replace( array("@<@", "@>@", "@'@", '@"@', "@&@", "@".LINEBREAK."@", "@\n@", "@\r@"), array("&lt;", "&gt;", "&apos;", "&quot;", "&amp;", '&#x0a;', '&#x0a;', '&#x0a;'), $value); return utf8_encode(preg_replace( array("@<@", "@>@", "@'@", '@"@', "@&@", "@".LINEBREAK."@", "@\n@", "@\r@"), array("&lt;", "&gt;", "&apos;", "&quot;", "&amp;", '&#x0a;', '&#x0a;', '&#x0a;'), $value)); } // end func xmlencode } // end class PhpdocXMLWriter ?>

« previous php.cvs (#1951) next »