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: | Sebastian Bergmann | 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("<", "<", $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("<", "<", $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("@<@", "@>@",
"@'@", "@"@", "@&@"),
array("<", ">", "'", '"',
"&"), $value);
return utf8_decode(preg_replace( array("@<@", "@>@",
"@'@", "@"@", "@&@"),
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("<", ">", "'",
""", "&", '
', '
',
'
'), $value);
return utf8_encode(preg_replace( array("@<@", "@>@",
"@'@", '@"@', "@&@",
"@".LINEBREAK."@", "@\n@", "@\r@"),
array("<", ">", "'", """,
"&", '
', '
', '
'),
$value));
} // end func xmlencode
} // end class PhpdocXMLWriter
?>