3 * Functions used to parse PHPDoc Tags.
\r
5 * @version $Id: PhpdocParserTags.php,v 1.4 2000/12/03 22:37:37 uw Exp $
\r
7 class PhpdocParserTags extends PhpdocParserRegExp {
\r
10 * Extract the value from the given tags and copy the data to the $data array if its an allowed tag
\r
12 * @param array $tags array of tags returned by getTags
\r
13 * @param array $data array where the allowed tags and their values are copied to
\r
14 * @param array $allowed array of allowed (recognized) tags
\r
15 * @return array $data
\r
16 * @throws PhpdocError
\r
17 * @see getTags(), analyseVariableParagraphs(), analyseFunctionParagraphs(), analyseClassParagraphs(), analyseSeeTags()
\r
19 function analyseTags($tags, $data, $allowed) {
\r
21 if (!is_array($tags) || !is_array($data) || !is_array($allowed)) {
\r
22 $this->err[] = new PhpdocError("Illegal function call", __FILE__, __LINE__);
\r
27 while (list($k, $tag) = each($tags)) {
\r
29 $tagname = substr( strtolower($tag["tag"]), 1 );
\r
30 if (!isset($allowed[$tagname])) {
\r
31 $data["notallowed"][$tagname] = true;
\r
37 # @tagname description
\r
47 if (isset($data[$tagname])) {
\r
49 $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used.";
\r
50 $data["syntaxerror"][] = $tag;
\r
55 if ("" == $tag["value"]) {
\r
57 $tag["msg"] = "Description is missing, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
58 $data["syntaxerror"][] = $tag;
\r
59 $data[$tagname] = $tag["value"];
\r
62 $data[$tagname] = $tag["value"];
\r
71 if (isset($data[$tagname])) {
\r
73 $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used.";
\r
74 $data["syntaxerror"][] = $tag;
\r
79 if ("" != $tag["value"]) {
\r
81 $tag["msg"] = "Description gets ignored, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'.";
\r
82 $data["syntaxerror"][] = $tag;
\r
85 $data[$tagname] = true;
\r
91 if (isset($data[$tagname])) {
\r
93 $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used.";
\r
94 $data["syntaxerror"][] = $tag;
\r
99 if (preg_match($this->TAGS[$tagname], $tag["value"], $regs)) {
\r
103 if ("object" == $regs[1]) {
\r
106 if ( "" == $regs[2] ) {
\r
108 $type .= "[unknown]";
\r
109 $tag["msg"] = "Objectname is missing, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
110 $data["syntaxerror"][] = $tag;
\r
123 $desc = $regs[2] . " " . $regs[4];
\r
127 $data[$tagname] = array (
\r
129 "name" => $regs[3],
\r
132 if ("" != trim($desc))
\r
133 $data[$tagname]["desc"] = $desc;
\r
137 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
138 $tag["msg"] .= $this->TAGS[$tagname];
\r
139 $data["syntaxerror"][] = $tag;
\r
146 if (preg_match($this->TAGS["global"], $tag["value"], $regs)) {
\r
148 if ("" == $regs[3]) {
\r
150 $tag["msg"] = "Variablename ist missing, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
151 $data["syntaxerror"][] = $tag;
\r
155 if ("object" == $regs[1]) {
\r
158 if ( "" == $regs[2] ) {
\r
160 $type .= "[unknown]";
\r
161 $tag["msg"] = "Objectname is missing, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
162 $data["syntaxerror"][] = $tag;
\r
176 if ("" == $regs[4]) {
\r
177 $data["global"][] = array (
\r
182 $data["global"][] = array (
\r
184 "name" => $regs[3],
\r
191 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
192 $data["syntaxerror"][] = $tag;
\r
201 if (preg_match($this->TAGS["var"], $tag["value"], $regs)) {
\r
203 if ("object" == $regs[1]) {
\r
206 if ("" == $regs[2]) {
\r
208 $type .= "[unknown]";
\r
209 $tag["msg"] = "Objectname is missing, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
210 $data["syntaxerror"][] = $tag;
\r
224 if ("" == $regs[4]) {
\r
225 $data["params"][] = array (
\r
230 $data["params"][] = array (
\r
232 "name" => $regs[3],
\r
239 $tag["msg"] = "General syntax error, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'.";
\r
240 $data["syntaxerror"][] = $tag;
\r
248 if ("" != $tag["value"]) {
\r
251 $references = explode(",", $tag["value"] );
\r
252 reset($references);
\r
253 while (list($k, $reference) = each($references)) {
\r
255 if (preg_match($this->TAGS["see_var"], $reference, $regs)) {
\r
257 list($msg, $entry) = $this->analyseSeeTagRegs($regs);
\r
259 if (count($entry) > 0)
\r
260 $data["see"]["var"][] = $entry;
\r
262 } else if (preg_match($this->TAGS["see_function"], $reference, $regs)) {
\r
264 list($msg, $entry) = $this->analyseSeeTagRegs($regs);
\r
266 if (count($entry) > 0)
\r
267 $data["see"]["function"][] = $entry;
\r
269 } else if (preg_match($this->TAGS["see_moduleclass"], $reference, $regs)) {
\r
273 if (substr($name, 0, $this->C_COMPLEX["module_separator_len"]) == $this->C_BASE["module_separator"]) {
\r
275 $name = substr($name, $this->C_COMPLEX["module_separator_len"]);
\r
278 $error .= "Element name is missing: '$regs[0]'. Reference gets ignored";
\r
283 $error .= "Element name starts with moduleseparator, module name forgotten '$regs[0]'?";
\r
288 } else if (!strstr($name, $this->C_BASE["module_separator"])) {
\r
290 $error .= "Use function() to referr to functions and $var to referr to variables - don't know what '$name' referrs to.";
\r
295 $data["see"]["moduleclass"][] = $name;
\r
299 $error .= "Unknown syntax '$reference'";
\r
305 if ( "" != $error) {
\r
307 $tag["msg"] = sprintf("Could not understand all references. %s. Syntax: '%s' (function), '%s' (variable), '%s' (module or class).",
\r
309 $this->C_COMPLEX["see_function"],
\r
310 $this->C_COMPLEX["see_var"],
\r
311 $this->C_COMPLEX["see_moduleclass"]
\r
313 $data["syntaxerror"][] = $tag;
\r
319 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
320 $data["syntaxerror"][] = $tag;
\r
327 if (preg_match($this->TAGS["link"], $tag["value"], $regs)) {
\r
329 $desc = trim($regs[2]);
\r
332 $data["link"][] = array(
\r
338 $data["link"][] = array(
\r
340 "desc" => trim($regs[2])
\r
347 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
348 $data["syntaxerror"][] = $tag;
\r
355 if ("" != $tag["value"]) {
\r
357 $exceptions = explode(",", $tag["value"]);
\r
358 reset($exceptions);
\r
359 while (list($k, $exception) = each($exceptions))
\r
360 $data["throws"][] = trim($exception);
\r
364 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
365 $data["syntaxerror"][] = $tag;
\r
372 if (preg_match($this->TAGS["access"], $tag["value"], $regs)) {
\r
374 $data["access"] = $regs[1];
\r
378 $tag["msg"] = ("" == $tag["value"]) ? "General syntax error," : "Access modifier unknown,";
\r
379 $tag["msg"].= " '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
380 $data["syntaxerror"][] = $tag;
\r
389 if (isset($data["deprec"])) {
\r
391 $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used.";
\r
392 $data["syntaxerror"][] = $tag;
\r
397 if ("" != $tag["value"]) {
\r
399 $data["deprec"] = $tag["value"];
\r
403 $tag["msg"] = "Description is missing, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
404 $data["syntaxerror"][] = $tag;
\r
412 if (isset($data["brother"])) {
\r
414 $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used.";
\r
415 $data["syntaxerror"][] = $tag;
\r
420 if ("" != $tag["value"]) {
\r
422 if (preg_match($this->TAGS["brother"], $tag["value"], $regs)) {
\r
424 $data["brother"] = $regs[1];
\r
428 $tag["msg"] = "Can't find a function name nor a variable name, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
429 $data["syntaxerror"][] = $tag;
\r
435 $data["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
436 $data["syntaxerror"][] = $tag;
\r
442 case "modulegroup":
\r
444 if (isset($data[$tagname])) {
\r
446 $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used.";
\r
447 $data["syntaxerror"][] = $tag;
\r
452 if ("" != $tag["value"]) {
\r
454 if (preg_match($this->TAGS["module"], $tag["value"], $regs)) {
\r
456 $data[$tagname] = $regs[0];
\r
460 $tag["msg"] = "Illegal label used, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
461 $data["syntaxerror"][] = $tag;
\r
467 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
468 $data["syntaxerror"][] = $tag;
\r
476 if (isset($data["const"])) {
\r
478 $tag["msg"] = "This tag must be used only once within a doc comment. First declaration gets used.";
\r
479 $data["syntaxerror"][] = $tag;
\r
484 if ("" != $tag["value"]) {
\r
486 if (preg_match($this->TAGS["const"], $tag["value"], $regs)) {
\r
488 $data["const"] = array(
\r
489 "name" => $regs[1],
\r
490 "desc" => trim($regs[2])
\r
495 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
496 $data["syntaxerror"][] = $tag;
\r
502 $tag["msg"] = "General syntax error, syntax: '" . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
503 $data["syntaxerror"][] = $tag;
\r
510 if ("" != $tag["value"]) {
\r
512 $authors = explode(",", $tag["value"]);
\r
514 while (list($k, $author) = each($authors)) {
\r
516 if (preg_match($this->TAGS["author"], $author, $regs)) {
\r
518 $data["author"][] = array(
\r
519 "name" => trim(substr($author, 0, strpos($author, $regs[0]))),
\r
520 "mail" => trim($regs[1])
\r
522 } else if (""!=trim($author)) {
\r
524 $data["author"][] = array(
\r
525 "name" => trim($author)
\r
530 $tag["msg"] = "Name is missing in enumeration, syntax: '".$this->PHPDOC_TAGS["@$tagname"]."'.";
\r
531 $data["syntaxerror"][] = $tag;
\r
539 $tag["msg"] = "General syntax error, syntax: " . $this->PHPDOC_TAGS["@$tagname"] . "'.";
\r
540 $data["syntaxerror"][] = $tag;
\r
547 // I'm quite sure this default is obsolete, but I don't feel like checking it.
\r
548 // Anyway this array index should get used one fine day.
\r
549 $data["unknown"][] = $tag;
\r
556 } // end func analyseTags
\r
559 * Helperfunction to analyse see tags
\r
561 * @param array Array return by preg_match()
\r
562 * @return array $see[0] = error, $see[1] = data
\r
563 * @see analyseTags()
\r
565 function analyseSeeTagRegs($regs) {
\r
568 $group = trim($regs[1]);
\r
569 $name = trim($regs[2]);
\r
571 if (substr($name, 0, $this->C_COMPLEX["module_separator_len"]) == $this->C_BASE["module_separator"]) {
\r
573 $name = substr($name, $this->C_COMPLEX["module_separator_len"]);
\r
576 $error = "Element name is missing '$regs[0]'. Reference gets ignored";
\r
577 return array($error, array());
\r
581 $error = "Element name starts with moduleseparator, module name forgotten '$regs[0]'?";
\r
587 if ("" != $group && $this->C_BASE["module_separator"] != $group) {
\r
589 $group = substr($group, 0, $this->C_COMPLEX["module_separator_len_neg"]);
\r
590 if ("" == $group) {
\r
592 $error = "Groupname missing '$regs[0]'.";
\r
593 $data = array( "name" => $name );
\r
606 $data = array ( "name" => $name );
\r
610 return array($error, $data);
\r
611 } // end func analyseSeeTagRegs
\r
614 * Extracts PHPDoc tags from a PHPDoc doc comment.
\r
616 * @param string Doc comment.
\r
617 * @return array List of tags ordered by their appearance containing the
\r
618 * tag name and it's (unparsed) value.
\r
621 function getTags($phpdoc) {
\r
623 $positions = $this->getTagPos($phpdoc);
\r
625 if (0 == count($positions))
\r
629 list($k, $data) = each($positions);
\r
630 $lastpos = $data["pos"];
\r
631 $lasttag = $data["tag"];
\r
633 while (list($k, $data) = each($positions)) {
\r
635 $line = substr($phpdoc, $lastpos, ($data["pos"] - $lastpos));
\r
636 $value = trim(substr($line, strlen($lasttag)));
\r
637 $tags[] = array ("tag" => $lasttag, "value" => $value );
\r
639 $lastpos = $data["pos"];
\r
640 $lasttag = $data["tag"];
\r
644 $line = substr($phpdoc, $lastpos);
\r
645 $value = trim(substr($line, strlen($lasttag)));
\r
646 $tags[] = array ("tag" => $lasttag, "value" => $value );
\r
649 } // end func getTags
\r
652 * Find the position of the next phpdoc tag.
\r
654 * @param string $phpdoc
\r
655 * @param integer $offset
\r
656 * @return array $tag 0 => tag, 1 => offset
\r
660 function getTagPos($phpdoc, $offset = 0) {
\r
662 $positions = array();
\r
664 preg_match_all($this->TAGS["all"], $phpdoc, $regs, PREG_SET_ORDER);
\r
667 while (list($k, $data) = each($regs)) {
\r
669 $pos = strpos($phpdoc, $data[0], $offset);
\r
671 if ($pos > 0 || $data[0] == substr($phpdoc, 0, strlen($data[0])) ) {
\r
672 $positions[] = array ("pos" => $pos, "tag" => $data[0]);
\r
679 } // end func getTagPos
\r
682 * Takes an array filled by analyseTags() and converts the parse errors into a single error message.
\r
684 * Checks for [syntaxerror], [notallowed] and [unknown] entries in the data hash,
\r
685 * converts them into an error message and unsets the indizes. The function
\r
686 * returns a hash containing the aggregates error message and the modified
\r
689 * @param array $data
\r
690 * @param string $mode Keyword where the data hash comes from eg. function/class...
\r
691 * @return array array( $error_msg, $data )
\r
693 function checkParserErrors($data, $mode) {
\r
696 // tags with an incorrect syntax
\r
697 if (isset($data["syntaxerror"])) {
\r
699 $msg.= "PHPDoc found " . count($data["syntaxerror"]) . " syntax error(s) in the tag list. ";
\r
701 reset($data["syntaxerror"]);
\r
702 while (list($k, $error) = each($data["syntaxerror"]))
\r
703 $msg.= sprintf("Tag: '%s %s' - %s.", $error["tag"], $error["value"], $error["msg"]);
\r
705 unset($data["syntaxerror"]);
\r
709 // tags that are not allowed in this context
\r
710 if (isset($data["notallowed"])) {
\r
712 $msg .= count($data["notallowed"]) . " tag[s] were used that are not allowed in $mode doc comments: ";
\r
714 reset($data["notallowed"]);
\r
715 while (list($tagname, $v) = each($data["notallowed"]))
\r
716 $msg .= "$tagname, ";
\r
718 $msg = substr($msg, 0, -2) . ".";
\r
719 unset($data["notallowed"]);
\r
723 if (isset($data["unknown"])) {
\r
725 $msg .= "PHPDoc found " . count($data["unknown"]) . " tag[s] that are unknown: ";
\r
727 reset($data["unknown"]);
\r
728 while (list($k, $error) = each($data["unknown"]))
\r
729 $msg.= sprintf("%s, ", $error["tag"]);
\r
731 $msg = substr($msg, 0, -2) . ".";
\r
732 unset($data["unknown"]);
\r
735 return array($msg, $data);
\r
736 } // end func checkParserErrors
\r
738 } // end class PhpdocParserTags
\r