uw Sun Feb 18 06:45:29 2001 EDT Modified files: /php4/pear/PHPDoc/parser PhpdocClassParser.php PhpdocConstantParser.php PhpdocFunctionParser.php PhpdocModuleParser.php PhpdocParser.php PhpdocParserCore.php PhpdocParserRegExp.php PhpdocParserTags.php PhpdocUseParser.php PhpdocVariableParser.php Log: Sorry, whitespace only changes to follow the PEAR Coding conventions. Replaces tabs with spaces.
Index: php4/pear/PHPDoc/parser/PhpdocClassParser.php diff -u php4/pear/PHPDoc/parser/PhpdocClassParser.php:1.2 php4/pear/PHPDoc/parser/PhpdocClassParser.php:1.3 --- php4/pear/PHPDoc/parser/PhpdocClassParser.php:1.2 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocClassParser.php Sun Feb 18 06:45:27 2001 @@ -2,127 +2,127 @@ /** * Parses phpcode to extract classes and their documentation. * -* @version $Id: PhpdocClassParser.php,v 1.2 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocClassParser.php,v 1.3 2001/02/18 14:45:27 uw Exp $ */ 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 - */ - 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) { + /** + * 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 + */ + 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"]); + $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 + 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 - + /** + * 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 diff -u php4/pear/PHPDoc/parser/PhpdocConstantParser.php:1.4 php4/pear/PHPDoc/parser/PhpdocConstantParser.php:1.5 --- php4/pear/PHPDoc/parser/PhpdocConstantParser.php:1.4 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocConstantParser.php Sun Feb 18 06:45:27 2001 @@ -2,108 +2,108 @@ /** * Extracts define statements and their documentation from php code. * -* @version $Id: PhpdocConstantParser.php,v 1.4 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocConstantParser.php,v 1.5 2001/02/18 14:45:27 uw Exp $ */ class PhpdocConstantParser extends PhpdocUseParser { - /** - * Internal structure use to save a constant. - * - * @var array - */ - var $emptyConstant = array( - "name" => "", - "value" => "", - "undoc" => true - ); - - /** - * Doc Tags allowed with const[ant]. - * - * @var array - */ - var $constantTags = array( - "access" => true, - "see" => true, - "link" => true, - - "constant" => true, - "const" => true, - - "author" => true, - "copyright" => true, - - "exclude" => true, - "magic" => true, - "todo" => true - ); - - /** - * Scans the given constant doc comment. - * - * @param array - */ - 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 - + /** + * Internal structure use to save a constant. + * + * @var array + */ + var $emptyConstant = array( + "name" => "", + "value" => "", + "undoc" => true + ); + + /** + * Doc Tags allowed with const[ant]. + * + * @var array + */ + var $constantTags = array( + "access" => true, + "see" => true, + "link" => true, + + "constant" => true, + "const" => true, + + "author" => true, + "copyright" => true, + + "exclude" => true, + "magic" => true, + "todo" => true + ); + + /** + * Scans the given constant doc comment. + * + * @param array + */ + 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 diff -u php4/pear/PHPDoc/parser/PhpdocFunctionParser.php:1.2 php4/pear/PHPDoc/parser/PhpdocFunctionParser.php:1.3 --- php4/pear/PHPDoc/parser/PhpdocFunctionParser.php:1.2 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocFunctionParser.php Sun Feb 18 06:45:27 2001 @@ -2,136 +2,137 @@ /** * Looks for documented and undocumented functions within a block of php code. * -* @version $Id: PhpdocFunctionParser.php,v 1.2 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocFunctionParser.php,v 1.3 2001/02/18 14:45:27 uw Exp $ */ class PhpdocFunctionParser extends PhpdocVariableParser { - /** - * Internal structur of a function. - * - * @var array $emptyFunction - */ - 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 - + /** + * Internal structur of a function. + * + * @var array $emptyFunction + */ + 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 -?> \ No newline at end of file Index: php4/pear/PHPDoc/parser/PhpdocModuleParser.php diff -u php4/pear/PHPDoc/parser/PhpdocModuleParser.php:1.1 php4/pear/PHPDoc/parser/PhpdocModuleParser.php:1.2 --- php4/pear/PHPDoc/parser/PhpdocModuleParser.php:1.1 Sun Oct 8 03:03:19 2000 +++ php4/pear/PHPDoc/parser/PhpdocModuleParser.php Sun Feb 18 06:45:27 2001 @@ -1,141 +1,142 @@ <?php /** * Extracts modules and their documentation from php code. -* @author Ulf Wendel <[EMAIL PROTECTED]> +* @author Ulf Wendel <[EMAIL PROTECTED]> * @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 - + /** + * 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 diff -u php4/pear/PHPDoc/parser/PhpdocParser.php:1.2 php4/pear/PHPDoc/parser/PhpdocParser.php:1.3 --- php4/pear/PHPDoc/parser/PhpdocParser.php:1.2 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocParser.php Sun Feb 18 06:45:27 2001 @@ -4,447 +4,447 @@ * * Note that a lot of communication is done using shared instance variables. * -* @version $Id: PhpdocParser.php,v 1.2 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocParser.php,v 1.3 2001/02/18 14:45:27 uw Exp $ */ 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 - * @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 - + /** + * 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 + * @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 diff -u php4/pear/PHPDoc/parser/PhpdocParserCore.php:1.3 php4/pear/PHPDoc/parser/PhpdocParserCore.php:1.4 --- php4/pear/PHPDoc/parser/PhpdocParserCore.php:1.3 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocParserCore.php Sun Feb 18 06:45:27 2001 @@ -5,657 +5,658 @@ * Provides basic parser functions to extract doc comments, analyse tags and variable * declarations. * -* @version $Id: PhpdocParserCore.php,v 1.3 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocParserCore.php,v 1.4 2001/02/18 14:45:27 uw Exp $ */ 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) { - - // find the position of the first doc tag - $positions = $this->getTagPos($phpdoc); - - if (0 == count($positions)) - $desc = trim($phpdoc); // no doc tags - else - $desc = trim(substr($phpdoc, 0, $positions[0]["pos"])); // strip tags - - $lines = split($this->PHP_BASE["break"], $desc); - - if (1 == count($lines) || "" == $desc) { - - // only a short description but no long description - or even none of both - $description = array ($desc, ""); - - } else { - - $sdesc = trim($lines[0]); - unset($lines[0]); - - $description = array ( $sdesc, implode("", $lines) ); - - } - - 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 - */ - 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 - + + /** + * 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 and 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 structure: $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) { + + // find the position of the first doc tag + $positions = $this->getTagPos($phpdoc); + + if (0 == count($positions)) + $desc = trim($phpdoc); // no doc tags + else + $desc = trim(substr($phpdoc, 0, $positions[0]["pos"])); // strip tags + + $lines = split($this->PHP_BASE["break"], $desc); + + if (1 == count($lines) || "" == $desc) { + + // only a short description but no long description - or even none of both + $description = array ($desc, ""); + + } else { + + $sdesc = trim($lines[0]); + unset($lines[0]); + + $description = array ($sdesc, implode("", $lines)); + + } + + 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 + */ + 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 diff -u php4/pear/PHPDoc/parser/PhpdocParserRegExp.php:1.4 php4/pear/PHPDoc/parser/PhpdocParserRegExp.php:1.5 --- php4/pear/PHPDoc/parser/PhpdocParserRegExp.php:1.4 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocParserRegExp.php Sun Feb 18 06:45:28 2001 @@ -7,486 +7,486 @@ * possible I decided to define all regular expressions in one class. * From a programming point of view there's no need to do so. * -* @version $Id: PhpdocParserRegExp.php,v 1.4 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocParserRegExp.php,v 1.5 2001/02/18 14:45:28 uw Exp $ */ 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 - // + /** + * 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["var"] = sprintf("/%s(?:%s(%s))?(?:%s(%s))?%s(.*)?/is", - $this->C_BASE["vartype"], - $this->PHP_BASE["space"], - $this->PHP_BASE["label"], - $this->PHP_BASE["space"], - $this->PHP_COMPLEX["varname"], - $this->PHP_BASE["space_optional"] - ); - $this->TAGS["return"] = $this->TAGS["var"]; - - $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_optional"] - ); - - $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]); + $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["var"] = sprintf("/%s(?:%s(%s))?(?:%s(%s))?%s(.*)?/is", + $this->C_BASE["vartype"], + $this->PHP_BASE["space"], + $this->PHP_BASE["label"], + $this->PHP_BASE["space"], + $this->PHP_COMPLEX["varname"], + $this->PHP_BASE["space_optional"] + ); + $this->TAGS["return"] = $this->TAGS["var"]; + + $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_optional"] + ); + + $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 func buildComplexRegExps + } // end class PhpdocParserRegExp ?> Index: php4/pear/PHPDoc/parser/PhpdocParserTags.php diff -u php4/pear/PHPDoc/parser/PhpdocParserTags.php:1.4 php4/pear/PHPDoc/parser/PhpdocParserTags.php:1.5 --- php4/pear/PHPDoc/parser/PhpdocParserTags.php:1.4 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocParserTags.php Sun Feb 18 06:45:28 2001 @@ -2,738 +2,735 @@ /** * Functions used to parse PHPDoc Tags. * -* @version $Id: PhpdocParserTags.php,v 1.4 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocParserTags.php,v 1.5 2001/02/18 14:45:28 uw Exp $ */ 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)) { - - $desc = ""; - - 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]; - - } - - $desc = $regs[4]; - - } else { - - $type = $regs[1]; - $desc = $regs[2] . " " . $regs[4]; - - } - - $data[$tagname] = array ( - "type" => $type, - "name" => $regs[3], - ); - - if ("" != trim($desc)) - $data[$tagname]["desc"] = $desc; - - } else { - - $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'."; - $tag["msg"] .= $this->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]'?"; - continue; - - } - - } else if (!strstr($name, $this->C_BASE["module_separator"])) { - - $error .= "Use function() to referr to functions and $var to referr to variables - don't know what '$name' referrs to."; - continue; - - } - - $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 = trim($regs[1]); - $name = trim($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 integer $offset - * @return array $tag 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 - + + /** + * 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)) { + + $desc = ""; + + 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]; + + } + + $desc = $regs[4]; + + } else { + + $type = $regs[1]; + $desc = $regs[2] . " " . $regs[4]; + + } + + $data[$tagname] = array ( + "type" => $type, + "name" => $regs[3], + ); + + if ("" != trim($desc)) + $data[$tagname]["desc"] = $desc; + + } else { + + $tag["msg"] = "General syntax error, syntax: '" . +$this->PHPDOC_TAGS["@$tagname"] . "'."; + $tag["msg"] .= $this->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]'?"; + continue; + + } + + } else if (!strstr($name, +$this->C_BASE["module_separator"])) { + + $error .= "Use function() to referr to functions +and $var to referr to variables - don't know what '$name' referrs to."; + continue; + + } + + $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 $see[0] = error, $see[1] = data + * @see analyseTags() + */ + function analyseSeeTagRegs($regs) { + + $error = ""; + $group = trim($regs[1]); + $name = trim($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 integer $offset + * @return array $tag 0 => tag, 1 => offset + * @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 diff -u php4/pear/PHPDoc/parser/PhpdocUseParser.php:1.1 php4/pear/PHPDoc/parser/PhpdocUseParser.php:1.2 --- php4/pear/PHPDoc/parser/PhpdocUseParser.php:1.1 Sun Oct 8 03:03:19 2000 +++ php4/pear/PHPDoc/parser/PhpdocUseParser.php Sun Feb 18 06:45:28 2001 @@ -1,74 +1,78 @@ <?php /** * Extracts use statements (include and friends) an thheir documentation from php code. -* @author Ulf Wendel <[EMAIL PROTECTED]> -* @version 0.1alpha +* +* @author Ulf Wendel <[EMAIL PROTECTED]> +* @version $Id: PhpdocUseParser.php,v 1.2 2001/02/18 14:45:28 uw Exp $ */ class PhpdocUseParser extends PhpdocParserCore { - /** - * Structure of an empty use entry. - * @var array - */ - var $emptyUse = array( - "type" => "", - "file" => "", - "undoc" => true - ); - + /** + * 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, + /** + * 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 - ); + "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"]; + /** + * 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 - + return $use; + } // end func analyseUse + } // end class PhpdocUseParser ?> Index: php4/pear/PHPDoc/parser/PhpdocVariableParser.php diff -u php4/pear/PHPDoc/parser/PhpdocVariableParser.php:1.3 php4/pear/PHPDoc/parser/PhpdocVariableParser.php:1.4 --- php4/pear/PHPDoc/parser/PhpdocVariableParser.php:1.3 Sun Dec 3 14:37:37 2000 +++ php4/pear/PHPDoc/parser/PhpdocVariableParser.php Sun Feb 18 06:45:28 2001 @@ -2,126 +2,128 @@ /** * Extract class variables and their documentation from phpcode * -* @version $Id: PhpdocVariableParser.php,v 1.3 2000/12/03 22:37:37 uw Exp $ +* @version $Id: PhpdocVariableParser.php,v 1.4 2001/02/18 14:45:28 uw Exp $ */ class PhpdocVariableParser extends PhpdocModuleParser { - /** - * Array with default values of a variable - * - * @var array $emptyVariable - */ - var $emptyVariable = array( - "name" => "", - "undoc" => true - ); + /** + * Array with default values of a variable + * + * @var array $emptyVariable + */ + 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 - ); + /** + * 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 - */ + /** + * 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; - - if ("unknown" != $value) - $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"] && "mixed" != $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]%s'.", - $variable["type"], - $variable["name"], - (isset($variable["var"]["desc"])) ? " " . $variable["var"]["desc"] : "" - ); - $this->warn->addDocWarning($this->currentFile, "variable", $variable["name"], $msg, "mismatch"); - - } else if ("unknown" == $variable["type"] && "" != $variable["var"]["type"]) { - - $variable["type"] = $variable["var"]["type"]; - - } - - if ("" != $variable["var"]["name"] && $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]%s'.", - ("" != $variable["var"]["type"]) ? $variable["var"]["type"] : $variable["type"], - $variable["name"], - (isset($variable["var"]["desc"])) ? " " . $variable["var"]["desc"] : "" - ); - $this->warn->addDocWarning($this->currentFile, "variable", $variable["name"], $msg, "mismatch"); - } - - - unset($variable["var"]); - - return $variable; - } // end func checkVarDocs + + $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; + + if ("unknown" != $value) + $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"] && "mixed" != $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]%s'.", + $variable["type"], + $variable["name"], + (isset($variable["var"]["desc"])) ? " " . +$variable["var"]["desc"] : "" + ); + $this->warn->addDocWarning($this->currentFile, "variable", +$variable["name"], $msg, "mismatch"); + + } else if ("unknown" == $variable["type"] && "" != $variable["var"]["type"]) { + + $variable["type"] = $variable["var"]["type"]; + + } + + if ("" != $variable["var"]["name"] && $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]%s'.", + ("" != $variable["var"]["type"]) ? +$variable["var"]["type"] : $variable["type"], + $variable["name"], + (isset($variable["var"]["desc"])) ? " " . +$variable["var"]["desc"] : "" + ); + $this->warn->addDocWarning($this->currentFile, "variable", +$variable["name"], $msg, "mismatch"); + } + + + unset($variable["var"]); + + return $variable; + } // end func checkVarDocs } // end class PhpdocVariableParser ?>
-- PHP CVS Mailing List (http://www.php.net/) To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] To contact the list administrators, e-mail: [EMAIL PROTECTED]