home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 August / PCWorld_2001-08_cd.bin / Komunikace / phptriad / phptriadsetup2-11.exe / php / pear / PHPDoc / analyser / PhpdocClassAnalyser.php < prev    next >
PHP Script  |  2001-02-18  |  23KB  |  553 lines

  1. <?php
  2. /**
  3. * Analyses a class.
  4. * @version    $Id: PhpdocClassAnalyser.php,v 1.5 2001/02/18 15:19:15 uw Exp $
  5. */
  6. class PhpdocClassAnalyser extends PhpdocAnalyser {
  7.  
  8.     /**
  9.     * Class data.
  10.     *
  11.     * @var  array
  12.     */
  13.     var $classes = array();
  14.     
  15.     /**
  16.     * Name of the baseclass of the given classes.
  17.     *
  18.     * @var  string    
  19.     */
  20.     var $baseclass = "";
  21.     
  22.     /**
  23.     * Ordered list of all classes.
  24.     *
  25.     * @var  array
  26.     */ 
  27.     var $classlist = array();
  28.     
  29.     /**
  30.     * List of not inherited elements.
  31.     *
  32.     * @var  array
  33.     */
  34.     var $notinherited = array(
  35.                                 "class"    => array(
  36.                                                     "name"          => true,
  37.                                                     "extends"       => true,
  38.                                                     "undoc"         => true,
  39.                                                     "variables"     => true,
  40.                                                     "functions"     => true,
  41.                                                     "consts"        => true,
  42.                                                     "uses"          => true,
  43.                                                     "filename"      => true,
  44.                                                     "subclasses"    => true,
  45.                                                     "path"          => true,
  46.                                                     "baseclass"     => true,
  47.                                                     "abstract"      => true
  48.                                                 ),
  49.                                                                 
  50.                                 "functions"    => array(
  51.                                                         "name"      => true,
  52.                                                         "undoc"     => true,
  53.                                                         "inherited" => true,
  54.                                                         "overrides" => true,
  55.                                                         "abstract"  => true
  56.                                                     ),
  57.                                                                         
  58.                                 "variables"    => array(
  59.                                                         "name"      => true,
  60.                                                         "undoc"     => true,
  61.                                                         "inherited" => true,
  62.                                                         "overrides" => true,
  63.                                                         "abstract"  => true
  64.                                                     ),    
  65.                                                                         
  66.                                 "uses"            => array(
  67.                                                             "name"      => true,
  68.                                                             "undoc"     => true,
  69.                                                             "inherited" => true,
  70.                                                             "overrides" => true
  71.                                                         ),                                                                                                                                                                                                                                
  72.                                 
  73.                                 "consts"    => array(
  74.                                                     "name"      => true,
  75.                                                     "undoc"     => true,
  76.                                                     "inherited" => true,
  77.                                                     "overrides" => true
  78.                                                 )                                                                                                                        
  79.                         );
  80.                                                     
  81.  
  82.     /**
  83.     * Puuuh - findUndocumented() needs this.
  84.     *
  85.     * @var  array
  86.     * @see  findUndocumented()
  87.     */                                                        
  88.     var $undocumentedFields = array(
  89.                                     "functions"    => "function",
  90.                                     "variables"    => "variable",
  91.                                     "uses"            => "included file",
  92.                                     "consts"        => "constant"
  93.                             );
  94.  
  95.     /**
  96.     * Sets the class data and the name of the baseclass.
  97.     *
  98.     * @param    array   Raw class data from the parser
  99.     * @param    string  Name of the baseclass of the given classes
  100.     * @access   public
  101.     */                                                        
  102.     function setClasses($classes, $baseclass) {
  103.         
  104.         $this->classes = $classes;
  105.         $this->baseclass = $baseclass;
  106.         
  107.     } // end func setClasses
  108.                                                     
  109.     function analyse() {
  110.         
  111.         $this->flag_get = false;
  112.  
  113.         $this->updateAccessReturn();
  114.         $this->updateBrothersSisters();
  115.         $this->checkSee();
  116.         
  117.         $this->classlist = array();
  118.         
  119.         $this->buildBottomUpClasslist($this->baseclass);
  120.         
  121.     } // end func analyse
  122.  
  123.     /**
  124.     * Returns an analysed class or false if there're no classes any more.
  125.     *
  126.     * @return   mixed   False if there no classes anymore, otherwise an array with 
  127.     *                   the data of the class.
  128.     * @accesss  public
  129.     */    
  130.     function getClass() {
  131.     
  132.         if (!$this->flag_get) {
  133.             reset($this->classlist);
  134.             $this->flag_get = true;
  135.         }
  136.         if (list($k, $classname) = each($this->classlist)) {
  137.  
  138.             if (isset($this->classes[$classname]["path"]))
  139.                 $this->inheritClassElements($classname);    
  140.             
  141.             $this->checkFunctionArgs($classname);
  142.             $this->findUndocumented($classname);
  143.             
  144.             $class = $this->classes[$classname];
  145.             unset($this->classes[$classname]);
  146.             return $class;
  147.             
  148.         } else {
  149.         
  150.             return false;
  151.             
  152.         }
  153.     } // end func getClass
  154.  
  155.     /**
  156.     * Looks for undocumented elements in a certain class
  157.     * 
  158.     * @param    string  Classname
  159.     */
  160.     function findUndocumented($classname) {
  161.         
  162.         $file = $this->classes["filename"];
  163.         if ($this->classes["undoc"])
  164.             $this->warn->addDocWarning($file, "class", $name, "The class is not documented.", "missing");
  165.             
  166.         reset($this->undocumentedFields);
  167.         while (list($index, $eltype) = each($this->undocumentedFields)) {
  168.             if (!isset($this->classes[$index]))
  169.                 continue;
  170.                 
  171.             reset($this->classes[$index]);
  172.             while (list($elname, $data) = each($this->classes[$index]))
  173.                 if (isset($data["undoc"]) && $data["undoc"])
  174.                     $this->warn->addDocWarning($file, $eltype, $elname, "Undocumented element.", "missing");
  175.                     
  176.         }
  177.         
  178.     } // end func findUndocumented
  179.     
  180.     /**
  181.     * Checks the function documentation of a certain class.
  182.     *
  183.     * @param    string  Classname
  184.     */
  185.     function checkFunctionArgs($classname) {
  186.  
  187.         if (!isset($this->classes[$classname]["functions"]))
  188.             return;
  189.                 
  190.         $file = $this->classes[$classname]["filename"];
  191.             
  192.         reset($this->classes[$classname]["functions"]);
  193.         while (list($fname, $function) = each($this->classes[$classname]["functions"])) {
  194.  
  195.             $inherited = isset($function["paraminherited"]);            
  196.             $this->classes[$classname]["functions"][$fname]["params"] = $this->checkArgDocs($function["args"], $function["params"], $fname, $file, $inherited);
  197.             unset($this->classes[$classname]["functions"][$fname]["args"]);
  198.  
  199.             if ($inherited)
  200.                 unset($this->classes[$classname]["functions"][$fname]["paraminherited"]);
  201.                 
  202.         }
  203.     } // end func checkFunctionArgs
  204.     
  205.     /**
  206.     * Builds an internal list of all classes.
  207.     * 
  208.     * The analyser needs an ordered list of all classes
  209.     * to inherit information effective.
  210.     * 
  211.     * @param    string      Name of the class that starts the recursive build process. 
  212.     * @see      $classlist
  213.     */
  214.     function buildBottomUpClasslist($classname) {
  215.         
  216.         if (isset($this->classes[$classname]["subclasses"])) {
  217.             
  218.             reset($this->classes[$classname]["subclasses"]);
  219.             while (list($subclass, $v) = each($this->classes[$classname]["subclasses"]))
  220.                 $this->buildBottomUpClasslist($subclass);
  221.             
  222.             $this->classlist[] = $classname;
  223.             
  224.         } else {
  225.          
  226.              $this->classlist[] = $classname;
  227.         
  228.         }
  229.     } // end func buildBottomUpClasslist
  230.  
  231.     /**
  232.     * Adds inherited elements to a class.
  233.     * 
  234.     * @param    string  Classname
  235.     * @return   boolean $ok
  236.     * @see      $classes, $notinherited, addInheritedElements()
  237.     */    
  238.     function inheritClassElements($classname) {
  239.         
  240.         if (!isset($this->classes[$classname]["path"]))
  241.             return false;
  242.  
  243.         $undoc = $this->classes[$classname]["undoc"];
  244.                 
  245.         $path = $this->classes[$classname]["path"];
  246.         reset($path);
  247.         while (list($k, $parentclass) = each($path)) {
  248.  
  249.             $this->addInheritedElements($classname, $parentclass, "functions");
  250.             $this->addInheritedElements($classname, $parentclass, "variables");
  251.             $this->addInheritedElements($classname, $parentclass, "consts");
  252.             $this->addInheritedElements($classname, $parentclass, "uses");
  253.             
  254.             reset($this->classes[$parentclass]);
  255.             while (list($field, $value) = each($this->classes[$parentclass])) 
  256.                 if (!isset($this->notinherited["class"][$field]) && !isset($this->classes[$classname][$field]))
  257.                     $this->classes[$classname][$field] = $value;
  258.             
  259.             if ($undoc && !$this->classes[$parentclass]["undoc"]) {
  260.                 $this->classes[$classname]["docinherited"] = true;
  261.                 $this->classes[$classname]["undoc"] = false;
  262.                 $undoc = false;
  263.             }
  264.             
  265.         }    
  266.         
  267.         return true;
  268.     } // end func inheritClassElements
  269.     
  270.     /**
  271.     * Adds inherited functions, variables, constants or included files to a class.
  272.     *  
  273.     * @param    string  Name of the class that inherits the informations.
  274.     * @param    string  Name of the parentclass
  275.     * @param    string  Type of elements inherited: "functions", "variables", "uses", "consts"
  276.     * @return   boolean $ok
  277.     * @see      $classes, $notinherited, isUndocumented()
  278.     */
  279.     function addInheritedElements($classname, $parentclass, $type) {
  280.     
  281.         if (!isset($this->classes[$parentclass][$type]))
  282.             return false;
  283.             
  284.         reset($this->classes[$parentclass][$type]);
  285.         while (list($elementname, $data) = each($this->classes[$parentclass][$type])) {
  286.             
  287.             if (!isset($this->classes[$classname][$type][$elementname])) {
  288.  
  289.                 $this->classes[$classname]["inherited"][$type][$parentclass][$elementname] = true;            
  290.  
  291.             } else {
  292.         
  293.                 $this->classes[$classname][$type][$elementname]["overrides"] = $parentclass;
  294.                 $this->classes[$classname][$type][$elementname]["undoc"] = $this->isUndocumented($parentclass, $type, $elementname);
  295.                 $this->classes[$classname]["overrides"][$type][$parentclass][$elementname] = true;
  296.                 
  297.                 reset($data);
  298.                 while (list($field, $value)=each($data)) {
  299.                 
  300.                     if (!isset($this->classes[$classname][$type][$elementname][$field]) && !isset($this->notinherited[$type][$field])) {
  301.                         $this->classes[$classname][$type][$elementname][$field] = $value;
  302.                         if ("params" == $field && "functions" == $type) $this->classes[$classname][$type][$elementname]["paraminherited"] = true;
  303.                     }
  304.                         
  305.                 }
  306.             }
  307.             
  308.         }
  309.         
  310.         return true;
  311.     } // end func addInheritedElements
  312.  
  313.     /**
  314.     * Returns true if the requested element is undocumented and false if it's documented.
  315.     *
  316.     * The function checks if the element might inherit documentation
  317.     * from any parentclass. 
  318.     *
  319.     * @param    string  Name of the class of the element
  320.     * @param    string  Element type: functions, variables, uses, consts.
  321.     * @param    string  Element name
  322.     * @return   boolean $ok
  323.     */    
  324.     function isUndocumented($classname, $type, $elementname) {
  325.  
  326.         if ( !isset($this->classes[$classname][$type][$elementname]) || $this->classes[$classname][$type][$elementname]["undoc"] || !isset($this->classes[$classname]["path"]) ) 
  327.             return true;
  328.         
  329.         $path = $this->classes[$classname]["path"];
  330.         while (list($k, $parentclass) = each($path))
  331.             if ($this->isUndocumented($parentclass, $type, $elementname))
  332.                 return true;
  333.         
  334.         return false;
  335.     } // end func isUndocumented
  336.     
  337.     function updateBrothersSisters() {
  338.         
  339.         reset($this->classes);
  340.         while (list($classname, $data) = each($this->classes)) {
  341.             $this->updateBrotherSisterElements($classname, "functions");
  342.             $this->updateBrotherSisterElements($classname, "variables");
  343.         }    
  344.         
  345.     } // end func updateBrothersSisters
  346.     
  347.     /**
  348.     * @param    string  Name of the class to update
  349.     * @param    string  Elementtype: functions, variables, ...
  350.     * @return   boolean
  351.     */
  352.     function updateBrotherSisterElements($classname, $type) {
  353.         
  354.         if (!isset($this->classes[$classname][$type])) 
  355.             return false;
  356.             
  357.         reset($this->classes[$classname][$type]);
  358.         while (list($elementname, $data) = each($this->classes[$classname][$type])) {
  359.             
  360.             if (isset($data["brother"])) {
  361.  
  362.                 $name = ( "functions" == $type ) ? substr($data["brother"], 0, -2) : substr($data["brother"], 1);
  363.                 $name = strtolower($name);
  364.  
  365.                 if (!isset($this->classes[$classname][$type][$name])) {
  366.                 
  367.                     $this->warn->addDocWarning($this->classes[$classname]["filename"], $type, $elementname, "Brother '$name' is unknown. Tags gets ignored.", "mismatch");
  368.                     unset($this->classes[$classname][$type][$elementname]["brother"]);
  369.                     
  370.                 } else {
  371.                 
  372.                     $this->classes[$classname][$type][$elementname]["brother"] = $name;
  373.                     $this->classes[$classname][$type][$elementname] = $this->copyBrotherSisterFields($this->classes[$classname][$type][$elementname], $this->classes[$classname][$type][$name]);
  374.  
  375.                 }
  376.  
  377.             }
  378.             
  379.         }
  380.         
  381.     } // end func updateBrotherSisterElements
  382.     
  383.     function updateAccessReturn() {
  384.         
  385.         reset($this->classes);
  386.         while (list($classname, $data) = each($this->classes)) {
  387.             
  388.             if (!isset($data["access"]))
  389.                 $this->classes[$classname]["access"] = "private";
  390.                 
  391.             $this->updateAccessReturnElements($classname, "functions");
  392.             $this->updateAccessElements($classname, "variables");
  393.             $this->updateAccessElements($classname, "consts");
  394.             
  395.         }
  396.                 
  397.     } // end func updateAccessReturn
  398.     
  399.     /**
  400.     * Updates access and return for certain elements.
  401.     * 
  402.     * This function should only be used to update functions.
  403.     * Functions that have the same name as the class (constructors)
  404.     * get return void and access public. Functions without 
  405.     * access get access public and functions without return get
  406.     * return void.
  407.     * 
  408.     * @param    string  Classname
  409.     * @param    string  Element type: functions (, variables, consts, uses)
  410.     * @return   boolean $ok
  411.     * @see      updateAccessReturn()
  412.     */
  413.     function updateAccessReturnElements($classname, $type) {
  414.         
  415.         if (!isset($this->classes[$classname][$type]))
  416.             return false;
  417.  
  418.         reset($this->classes[$classname][$type]);
  419.         while (list($elementname, $data) = each($this->classes[$classname][$type])) {
  420.         
  421.             if (!isset($data["access"])) 
  422.                 $this->classes[$classname][$type][$elementname]["access"] = ("functions" == $type && strtolower($elementname) == strtolower($classname)) ? "public" : "private";
  423.                 
  424.             if (!isset($data["return"]))
  425.                 $this->classes[$classname][$type][$elementname]["return"] = "void";
  426.             else 
  427.                 if ("functions" == $type && $elementname == $classname) {
  428.                     $this->warn->addDocWarning($this->classes[$classname]["filename"], "functions", $elementname, "The constructor can't have a return value. @return gets ignored.", "mismatch");
  429.                     $this->classes[$classname]["functions"][$elementname]["return"] = "void";
  430.                 }
  431.                 
  432.         }
  433.                 
  434.     } // end func updateAccessReturnElements
  435.     
  436.     /**
  437.     * Updates access tags.
  438.     *
  439.     * @param    string  Classname
  440.     * @param    string  Element type: functions, variables, consts (, uses)
  441.     * @see      updateAccessReturnElements()
  442.     */
  443.     function updateAccessElements($classname, $type) {
  444.         
  445.         if (!isset($this->classes[$classname][$type]))
  446.             return false;
  447.             
  448.         reset($this->classes[$classname][$type]);
  449.         while (list($elementname, $data) = each($this->classes[$classname][$type])) {
  450.             
  451.             if (!isset($data["access"])) 
  452.                 $this->classes[$classname][$type][$elementname]["access"] = ("functions" == $type && $elementname == $classname) ? "public" : "private";
  453.         
  454.         }
  455.         
  456.     } // end func updateAccessElements
  457.     
  458.     function checkSee() {
  459.         
  460.         reset($this->classes);
  461.         while (list($classname, $class) = each($this->classes)) {
  462.         
  463.             $this->buildElementlist($classname);
  464.             
  465.             if (isset($class["functions"])) 
  466.                 $this->checkSeeElements($class["functions"], $classname, "functions");
  467.                 
  468.             if (isset($class["variables"]))
  469.                 $this->checkSeeElements($class["variables"], $classname, "variables");
  470.             
  471.             if (isset($class["consts"])) 
  472.                 $this->checkSeeElements($class["consts"], $classname, "consts");
  473.                 
  474.             if (isset($class["uses"]))
  475.                 $this->checkSeeElements($class["uses"], $classname, "uses");
  476.             
  477.         }
  478.         
  479.     } // end func checkSee
  480.  
  481.     /**
  482.     * Checks see references in the given element array (functions, variables...)
  483.     *
  484.     * References to variables and functions within the same class get checked.
  485.     * It the references element does not exist, the reference gets deleted and 
  486.     * a doc warning gets generated.
  487.     * 
  488.     * @param    array   List of functions, variables,...
  489.     * @param    string  Name of the class that contains the given elements.
  490.     * @param    string  Elementtype: functions, variables, consts, uses.
  491.     */    
  492.     function checkSeeElements($elements, $classname, $eltype) {
  493.         
  494.         reset($elements);
  495.         while (list($elname, $element) = each($elements)) {
  496.         
  497.             if (isset($element["see"])) {
  498.                 
  499.                 if (isset($element["see"]["var"])) {
  500.                     
  501.                     reset($element["see"]["var"]);
  502.                     while (list($k, $variable) = each($element["see"]["var"])) 
  503.                         if (!isset($this->elementlist["variables"][strtolower($variable["name"])])) {
  504.                             $this->warn->addDocWarning($this->classes[$classname]["filename"], "variables", $elname, "@see referrs to the variable '" . $variable["name"] . "' which is not defined in the class. Entry gets ignored.", "mismatch");
  505.                             unset($this->classes[$classname][$eltype][$elname]["see"]["var"][$k]);
  506.                         }
  507.  
  508.                 }
  509.                 
  510.                 if (isset($element["see"]["function"])) {
  511.                     
  512.                     reset($element["see"]["function"]);
  513.                     while (list($k, $function) = each($element["see"]["function"]))
  514.                         if (!isset($this->elementlist["functions"][strtolower(substr($function["name"], 0, -2))])) {
  515.                             $this->warn->addDocWarning($this->classes[$classname]["filename"], "functions", $elname, "@see referrs to the function '" . $function["name"] . "' which is not defined in the class. Entry gets ignored.", "mismatch");
  516.                             unset($this->classes[$classname][$eltype][$elname]["see"]["function"][$k]);
  517.                         }
  518.  
  519.                 }
  520.                 
  521.             }
  522.             
  523.         }    
  524.         
  525.     } // end func checkSeeElement
  526.     
  527.     /**
  528.     * Builds an array with all elements of a class and saves it to $this->elementlist.
  529.     * 
  530.     * @param    string  Name of the class to scan.
  531.     */
  532.     function buildElementlist($classname) {
  533.         
  534.         $elements = array();
  535.         $fields = array("functions", "variables", "consts", "uses");
  536.         
  537.         reset($fields);
  538.         while (list($k, $field) = each($fields)) 
  539.             if (isset($this->classes[$classname][$field])) {
  540.                 
  541.                 reset($this->classes[$classname][$field]);
  542.                 while (list($element, ) = each($this->classes[$classname][$field])) 
  543.                     $elements[$field][$element] = true;
  544.                     
  545.             }
  546.         
  547.         $this->elementlist = $elements;
  548.         
  549.     } // end func buildElementlist
  550.  
  551. } // end class PhpdocClassAnalyser
  552. ?>