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

  1. <?php
  2. /**
  3. * "Main" class of the Parser collection.
  4. * Note that a lot of communication is done using shared instance variables.
  5. * @version    $Id: PhpdocParser.php,v 1.3 2001/02/18 14:45:27 uw Exp $
  6. */
  7. class PhpdocParser extends PhpdocClassParser {
  8.  
  9.     /**
  10.     * Name of the file currently parsed. 
  11.     * 
  12.     * Instead of passing the name of the current file by argument
  13.     * PHPDoc uses this slot to communicate. Yeah I know, it's
  14.     * the way methods should communicate, but it saves me a lot 
  15.     * a lot of work.
  16.     * @var    string    Name of the file currently parsed.
  17.     */
  18.     var $currentFile = "";
  19.     
  20.     /**
  21.     * Array of PHP Sourcecode Files to examine.
  22.     *
  23.     * The array keys hold the filenames, the array values the file content.
  24.     *
  25.     * @var    array
  26.     * @see    parse()
  27.     */
  28.     var    $phpfiles = array();
  29.     
  30.     /**
  31.     * Mapping from classnames to filenames
  32.     *
  33.     * @var    array
  34.     */
  35.     var $classnamesToFilenames = array();
  36.     
  37.     /**
  38.     * Hash with the data of the current class tree (one parentclass with all children).
  39.     *
  40.     * @var    array
  41.     * @see    $modules
  42.     */
  43.     var $classes = array();
  44.     
  45.     /**
  46.     * List of all parentclasses found.
  47.     * @var    array
  48.     */
  49.     var $baseclasses = array();
  50.  
  51.     /**
  52.     * List of all files containing classes.
  53.     *
  54.     * @var array
  55.     */        
  56.     var $classfiles = array();
  57.     
  58.     /**
  59.     * Hash of all class trees. 
  60.     *
  61.     * @var array
  62.     */
  63.     var $classtree = array();
  64.  
  65.     /**
  66.     * List of all files containing modules.
  67.     *
  68.     * @var    array
  69.     */    
  70.     var $modulefiles = array();
  71.     
  72.     /**
  73.     * List of all module groups.
  74.     *
  75.     * @var array
  76.     */
  77.     var $modulegroups = array();
  78.     
  79.     /**
  80.     * Hash with the data of the current module group.
  81.     *
  82.     * @var    array
  83.     * @see    $classes
  84.     */
  85.     var $modules = array();
  86.  
  87.     /**
  88.     * Hash of all packages found.
  89.     *
  90.     * @var    array
  91.     */    
  92.     var $packages = array();
  93.  
  94.     /**
  95.     * Flag indicating that getClassTree() was called.
  96.     *
  97.     * @var    boolean    
  98.     * @see    getClassTree()
  99.     */
  100.     var $flag_classtree = false;
  101.     
  102.     /**
  103.     * Flag indicating that getModulegroup was called.
  104.     *
  105.     * @var    boolean
  106.     * @see    getModulegroup()
  107.     */
  108.     var $flag_modulegroup = false;    
  109.     
  110.     /**
  111.     * Name of the base class of the current class tree.
  112.     *
  113.     * @var    string
  114.     * @see    getClassTree()
  115.     */
  116.     var $current_baseclass = "";
  117.     
  118.     /**
  119.     * Creates an instance of PhpdocWarning and calls buildComplexRegExps() to initialize the object.
  120.     *
  121.     * @param    boolean  If true the parser prints status messages.
  122.     * @see      $warn, buildComplexRegExps()
  123.     */
  124.     function PhpdocParser($flag_output = false) {
  125.     
  126.         if ($flag_output)
  127.             $this->setFlagOutput(true);
  128.         else 
  129.             $this->setFlagOutput(false);
  130.             
  131.         $this->buildComplexRegExps();
  132.         
  133.     } // end constructor
  134.     
  135.     /**
  136.     * Central parsing function.
  137.     *
  138.     * With version 0.3alpha PHPdoc changed the way the parser works. It does now
  139.     * 1 1/2 parsing runs. One prescan to build the class trees and a list of module
  140.     * groups and one deep scan to extract the information. This reduces the memory 
  141.     * consumption.
  142.     * 
  143.     * @return   boolean    $ok
  144.     * @access   public
  145.     * @see      findModulegroups(), findClassTrees(), getModulesAndClasses()
  146.     */
  147.     function preparse() {
  148.  
  149.         if (0 == count($this->phpfiles)) {
  150.             $this->err[] = new PHPDocError("Can't parse - no files defined.", __FILE__, __LINE__);
  151.             return false;
  152.         }
  153.         
  154.         $para = array();
  155.         reset($this->phpfiles);
  156.         while (list($filename, $phpcode) = each($this->phpfiles))
  157.             $para[$filename] = $this->getModulesAndClasses($phpcode);
  158.             
  159.         $this->findModulegroups($para);
  160.         $this->findClassTrees($para);
  161.         
  162.         return true;        
  163.     } // end func preparse
  164.     
  165.     /**
  166.     * Returns the data of one parentclass and all it's subclasses or false.
  167.     *
  168.     * Use this function to loop through the class trees. The loop should look somewhat like: 
  169.     * <code>while ( $classtree = $parser->getClassTree() ) ...</code>
  170.     *  
  171.     * @return   mixed   $classes    Hash with the data of the current class tree or false.
  172.     * @access   public
  173.     * @see      getModulegroup(), $baseclasses
  174.     */
  175.     function getClassTree() {
  176.     
  177.         // first call, reset the baseclass array pointer
  178.         if (!$this->flag_classtree) {
  179.             reset($this->baseclasses);
  180.             $this->flag_classtree = true;
  181.         }
  182.         
  183.         if (list($classname, $filename) = each($this->baseclasses)) {
  184.         
  185.             $this->classes = array();
  186.             $this->current_baseclass = $classname;
  187.             
  188.             $this->addClass($classname, $filename);
  189.  
  190.             return $this->classes;
  191.             
  192.         } else {
  193.         
  194.             return false;
  195.             
  196.         }
  197.         
  198.     } // end func getClassTree
  199.     
  200.     /**
  201.     * Returns the data of one module group.
  202.     * 
  203.     * Use this function to loop through the module groups. The loop should look somewhat like:
  204.     * <code>while ( $modulegroup = $parser->getModulegroup() ) ...</code>.
  205.     *
  206.     * @return   mixed        $modulegroup    Hash with the data of the current class tree or false.
  207.     * @access   public
  208.     * @see      getClassTree(), addModule(), $modulegroups
  209.     */
  210.     function getModulegroup() {
  211.         
  212.         if (!$this->flag_modulegroup) {
  213.             reset($this->modulegroups);
  214.             $this->flag_modulegroup = true;
  215.         }
  216.         
  217.         if (list($group, $modules) = each($this->modulegroups)) {
  218.             
  219.             $this->modules = array();
  220.             while (list($modulename, $files) = each($modules)) {
  221.                 reset($files);
  222.                 while (list($k, $filename) = each($files))
  223.                     $this->addModule($group, $filename);
  224.             }
  225.             
  226.             return $this->modules;
  227.             
  228.         } else {
  229.         
  230.             return false;
  231.         
  232.         }
  233.         
  234.     } // end func getModulegroup
  235.     
  236.     /**
  237.     *    Analyses the given file and adds the result to the module list.
  238.     * 
  239.     * The function analyses the given file, unsets the file in the 
  240.     * file list, adds the result of the parser to the module list and 
  241.     * if necessary it adds some data to the package list.
  242.     *
  243.     * @param    string    Name of the module group the parsing result gets added.
  244.     * @param    string    Name of the file to scan.
  245.     * @see    getPhpdocParagraphs(), analyseModule()
  246.     */    
  247.     function addModule($group, $filename) {
  248.  
  249.         $data = $this->getPhpdocParagraphs($this->phpfiles[$filename], array("classes", "variables") );
  250.         // free memory as soon as possible...
  251.         unset($this->phpfiles[$filename]);
  252.         
  253.         // note: not passed by argument
  254.         $this->currentFile = $filename;
  255.         $result = $this->analyseModule($data);
  256.         $result["filename"] = $filename;
  257.         
  258.         $this->modules[$group][$result["name"]] = $result;
  259.                     
  260.         if (isset($result["package"]))
  261.             $this->packages[$result["package"]]["modules"][] = array (
  262.                                                                       "name"        => $result["name"],
  263.                                                                       "group"       => $result["group"],
  264.                                                                       "filename"    => $filename
  265.                                                                  );
  266.         
  267.     } // end func addModule
  268.     
  269.     /**
  270.     * Analyses the given file and adds the result to the class list.
  271.     * 
  272.     * The first parameter (classname) comes from the prescan done 
  273.     * by findClassTrees()
  274.     *
  275.     * @param    string    Name of the class that gets added.
  276.     * @param    string    Name of the file to scan.
  277.     * @see      addSubclasses(), analyseClass(), $classes
  278.     */
  279.     function addClass($classname, $filename) {
  280.         
  281.         $data = $this->getPhpdocParagraphs($this->phpfiles[$filename], array("modules") );
  282.         // free memory as soon as possible...
  283.         unset($this->phpfiles[$filename]);
  284.         
  285.         $this->currentFile = $filename;
  286.         $result = $this->analyseClass($data);
  287.  
  288.         // Add some informations from the classtree that was build by the prescan to the class.
  289.         $fields = array("subclasses", "noparent", "path", "baseclass");
  290.         reset($fields);
  291.         while (list($k, $field) = each($fields))
  292.             if (isset($this->classtree[$filename][$classname][$field]))
  293.                 $result[$field] = $this->classtree[$filename][$classname][$field];
  294.  
  295.         $result["filename"] = $filename;
  296.         
  297.         $this->classes[$classname] = $result;
  298.         $this->addSubclasses($classname);
  299.         
  300.         if (isset($result["package"]))
  301.             $this->packages[$result["package"]]["classes"][] = $classname;
  302.                 
  303.     } // end func addClass
  304.     
  305.     /**
  306.     * Adds recursively subclasses to the specified class.
  307.     *
  308.     * @param    string Name of the class that might contain subclasses
  309.     * @see    addClass()
  310.     */
  311.     function addSubclasses($classname) {
  312.         
  313.         if (isset($this->classes[$classname]["subclasses"])) {
  314.             
  315.             $subclasses = $this->classes[$classname]["subclasses"];
  316.             while (list($subclass, $v) = each($subclasses)) 
  317.                 $this->addClass($subclass, $this->classnamesToFilenames[$subclass]);
  318.                 
  319.         }
  320.         
  321.     } // end func addSubclasses
  322.     
  323.     /**
  324.     * Builds the hash of module groups and the module file list.
  325.     *
  326.     * @param    array    Hash with the result of getClassesAndModules() of all files
  327.     * @see    parse(), findClassTree(), $modulegroups, $modulefiles
  328.     */
  329.     function findModulegroups($para) {
  330.         
  331.         reset($para);
  332.         while (list($filename, $data) = each($para)) {
  333.  
  334.             if (isset($data["modules"]["name"])) {
  335.             
  336.                 $name = ("" != $data["modules"]["name"]) ? $data["modules"]["name"] : $filename;
  337.                 $group = ("" != $data["modules"]["group"]) ? $data["modules"]["group"] : $name;
  338.                 
  339.                 if (0 != count($data["classes"])) {
  340.                     // As we do not have a real parser that returns a parsing tree we can't 
  341.                     // handle modules and classes in one file. Drop a note to the user.
  342.                     $this->warn->addDocWarning(    $filename, "module", $name, "PHPDoc is confused: module files must not contain classes. Doc will probably be broken, module gets ignored.", "collision" );
  343.                     continue;
  344.                 }
  345.  
  346.                 if (isset($this->modulegroups[$group][$name])) 
  347.                     $this->warn->addDocWarning($filename, "module", $name, "Warning: there's more than one module '$name' (file: '$filename) in the module group '$group'.", "warning");
  348.  
  349.                 $this->modulegroups[$group][$name][] = $filename;
  350.                 $this->modulefiles[] = $filename;
  351.  
  352.             }
  353.             
  354.         }
  355.  
  356.     } // end func findModulegroups
  357.     
  358.     /**
  359.     * Builds a hash of all class trees.
  360.     *
  361.     * @param    array    Hash with the result of getClassesAndModules() of all files
  362.     * @see      parse(), findModulegroups(), $classnamesToFilenames, $classtree, $classfiles, $baseclasses
  363.     */
  364.     function findClassTrees($para) {
  365.         
  366.         reset($para);
  367.         while(list($filename, $data) = each($para)) {
  368.         
  369.             if (0!=count($data["classes"])) {
  370.  
  371.                 $classname = $data["classes"][0]["name"];
  372.                                                             
  373.                 if (1<count($data["classes"]))
  374.                     $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");
  375.                     
  376.                 if (isset($data["modules"]["name"]))
  377.                     $this->warn->addDocWarning($filename, "class", "", "Warning: found a module comment in a class file. Module comment gets ignored, doc might be broken.", "collision");
  378.                 
  379.                 $this->classnamesToFilenames[$classname] = $filename;
  380.                 $this->classtree[$filename][$classname] = $data["classes"][0];
  381.                 $this->classfiles[] = $filename;
  382.  
  383.             }
  384.             
  385.         }
  386.         
  387.         reset($this->classnamesToFilenames);
  388.         while (list($classname, $filename)=each($this->classnamesToFilenames)) {
  389.  
  390.             $path             = array();
  391.             $baseclass    = $classname;
  392.             $basefile     = $filename;
  393.             $flag_noparent    = false;
  394.             
  395.             while ($extends = $this->classtree[$basefile][$baseclass]["extends"]) {
  396.                 if (!isset($this->classnamesToFilenames[$extends])) {
  397.                     $flag_noparent = true;
  398.                     break;
  399.                 }
  400.  
  401.                 $this->classtree[$this->classnamesToFilenames[$extends]][$extends]["subclasses"][$baseclass] = true;
  402.                 $path[]     = $extends;
  403.                 $baseclass  = $extends;
  404.                 $basefile   = $this->classnamesToFilenames[$baseclass];
  405.             }
  406.             
  407.             if ($flag_noparent)
  408.                 $this->classtree[$filename][$classname]["noparent"] = $flag_noparent;
  409.             
  410.             $base = (0 == count($path)) ? true : false;
  411.             if ($base) 
  412.                 $this->baseclasses[$classname] = $filename;
  413.             else 
  414.                 $this->classtree[$filename][$classname]["path"] = $path;
  415.                 
  416.             if ($baseclass != $classname)
  417.                 $this->classtree[$filename][$classname]["baseclass"] = $baseclass;
  418.         }
  419.         
  420.     } // end func findClassTrees
  421.  
  422.     /**
  423.     * Returns the mapping array from classnames to filenames
  424.     *
  425.     * @return array    
  426.     * @see        $classnamesToFilenames
  427.     */
  428.     function getClassnamesToFilenames() {
  429.         return $this->classnamesToFilenames;
  430.     } // end func getClassnamesToFilenames
  431.  
  432.     
  433.     /**
  434.     * Sets the list of PHP Soucecode Files to examine.
  435.     * @param    array        $phpfiles
  436.     * @return    bool        $ok
  437.     * @access    public
  438.     */
  439.     function setPhpSourcecodeFiles($phpfiles) {
  440.         if (!is_array($phpfiles) || 0 == count($phpfiles)) 
  441.             return false;
  442.         
  443.         $this->phpfiles = $phpfiles;
  444.         return true;    
  445.     } // end func setPhpSourcecodeFiles
  446.     
  447. } // end class PhpdocParser
  448. ?>