QA_Peardoc_Coverage
[ class tree: QA_Peardoc_Coverage ] [ index: QA_Peardoc_Coverage ] [ all elements ]

Source for file Coverage.php

Documentation is available at Coverage.php

  1. <?php
  2. require_once 'QA/Peardoc/Coverage/ClassList.php';
  3. require_once 'QA/Peardoc/Coverage/MethodList.php';
  4.  
  5. /*
  6. <!-- missing entities -->
  7. <!ENTITY copy "(C)">
  8. <!ENTITY agrave "">
  9. <!ENTITY dollar "$">
  10. <!ENTITY eacute "">
  11. <!ENTITY euro "€">
  12. */
  13.  
  14. /**
  15. *   Simple peardoc coverage analysis.
  16. *   Compares the classes and methods in PEAR packages
  17. *   with the PEAR documentation, trying to find
  18. *   out which packages aren't documented at all,
  19. *   where documentation of parts is lacking or
  20. *   which packages are fully documented.
  21. *
  22. *   IDs in peardoc:
  23. *   ---------------
  24. *   $packagename has "_" replaced with "-"
  25. *
  26. *   = Class:
  27. *   package.$category.$packagename
  28. *       package.html.html-form
  29. *   package.$category.$shortpackagename
  30. *       package.gtk.filedrop
  31. *
  32. *   = Methods:
  33. *   package.$category.$packagename.$methodname
  34. *       package.html.html-template-it.show
  35. *       package.html.html-template-it.show.desc
  36. *       package.html.html-template-it.show.parameter
  37. *       package.html.html-template-it.show.throws
  38. *   package.$category.$packagename.$classname.$methodname
  39. *       package.html.html-quickform.html-quickform.addelement
  40. *
  41. *   = Constructor:
  42. *   package.$category.$packagename.constructor
  43. *       package.gtk2.entrydialog.constructor
  44. *   package.$category.$packagename.$classname.$classname
  45. *       package.datetime.calendar.calendar-day.calendar-day
  46. *
  47. *
  48. *   TODO:
  49. *   - differentiate between stable and unstable packages
  50. *       (read package.xml)
  51. *
  52. *   @author Christian Weiske <cweiske@php.net>
  53. */
  54. class QA_Peardoc_Coverage
  55. {
  56.     /**
  57.     *   Special category associations.
  58.     *
  59.     *   key: package name
  60.     *   value: category name
  61.     */
  62.     public static $arCategoryAssociation = array(
  63.         'benchmark'                 => 'benchmarking',
  64.         'calendar'                  => 'datetime',
  65.         'games_chess'               => 'structures',
  66.         'genealogy_gedcom'          => 'fileformats',
  67.         'fsm'                       => 'processing',
  68.         'i18nv2'                    => 'internationalization',
  69.         'inline_c'                  => 'php',
  70.         'message'                   => 'encryption',
  71.         'mime_type'                 => 'tools',
  72.         'ole'                       => 'structures',
  73.         'pager'                     => 'html',
  74.         'pager_sliding'             => 'html',
  75.         'pecl_gen'                  => 'php',
  76.         'phpdoc'                    => 'tools',
  77.         'phpdocumentor'             => 'tools',
  78.         'safe_html'                 => 'html',
  79.         'search_mnogosearch'        => 'tools',
  80.         'selenium'                  => 'testing',
  81.         'spreadsheet_excel_writer'  => 'fileformats',
  82.         'sql_parser'                => 'database',
  83.         'translation'               => 'internationalization',
  84.         'translation2'              => 'internationalization',
  85.         'tree'                      => 'structures',
  86.         'xml_rpc'                   => 'webservices',
  87.         'xml_rpc2'                  => 'webservices',
  88.     );
  89.  
  90.     /**
  91.     *   List with package category name => Doc category name
  92.     *   associations.
  93.     *
  94.     *   Doc category assignments may be arrays of strings.
  95.     *
  96.     *   @var array 
  97.     */
  98.     public static $arCategoryDocNames = array(
  99.         'archive'       => 'fileformats',
  100.         'auth'          => 'authentication',
  101.         'cache'         => 'caching',
  102.         'codegen'       => 'tools',
  103.         'config'        => 'configuration',
  104.         'contact'       => 'fileformats',
  105.         'crypt'         => 'encryption',
  106.         'date'          => 'datetime',
  107.         'db'            => 'database',
  108.         'dba'           => 'database',
  109.         'file'          => array('fileformats''filesystem'),
  110.         'i18n'          => 'internationalization',
  111.         'image'         => 'images',
  112.         'liveuser'      => 'authentication',
  113.         'log'           => 'logging',
  114.         'mdb'           => 'database',
  115.         'mdb2'          => 'database',
  116.         'mp3'           => 'fileformats',
  117.         'net'           => 'networking',
  118.         'rdf'           => 'semanticweb',
  119.         'services'      => 'webservices',
  120.         'soap'          => 'webservices',
  121.         'stream'        => 'streams',
  122.     );
  123.  
  124.     /**
  125.     *   Lowercase package names that should be ignored.
  126.     *   @var array 
  127.     */
  128.     public static $arIgnoredPackages = array(
  129.         'forum',
  130.         'perm_liveuser'
  131.     );
  132.  
  133.  
  134.  
  135.     /**
  136.     *   Creates a new coverage checker instance.
  137.     *
  138.     *   @param string $strManualPath    Full path to the manual.xml file
  139.     *                                     of the pear documentation.
  140.     */
  141.     public function __construct($strManualPath$strPearDir)
  142.     {
  143.         $this->strManualPath $strManualPath;
  144.         $this->strPearDir    $strPearDir;
  145.     }//public function __construct($strManualPath, $strPearDir)
  146.  
  147.  
  148.  
  149.     /**
  150.     *   Generates the coverage analysis
  151.     *
  152.     *   @return array   Documentation coverage array. Pass it to a render() method.
  153.     */
  154.     public function generateCoverage()
  155.     {
  156.         $this->loadXmlDocument($this->strManualPath);
  157.  
  158.         /*
  159.         *   This is an array with doc statistics.
  160.         *   key:   category
  161.         *   value: array
  162.         *       key:   package name
  163.         *       value: array
  164.         *           special key:   '*docid*'
  165.         *           special value: documentation id
  166.         *                           NULL if no doc
  167.         *           special key:   '*path*'
  168.         *           special value: path to package cvs
  169.         *           key:   classname
  170.         *           value: array, NULL if no doc
  171.         *               key:   methodname
  172.         *               value: ??
  173.         */
  174.         $arDoc = array();
  175.  
  176.         foreach (self::getPackageList($this->strPearDir)
  177.                     as $strPackageDir => $strPackage
  178.         {
  179.             if (in_array(strtolower($strPackage)self::$arIgnoredPackages)) {
  180.                 continue;
  181.             }
  182.  
  183.             $arCategories = self::getCategory($strPackage);
  184.             foreach ($arCategories as $strCategory{
  185.                 $strId $this->getPackageDocId($strPackage$strCategory);
  186.                 if ($strId !== null{
  187.                     break;
  188.                 }
  189.             }
  190.  
  191.             if ($strId === null{
  192.                 $arDoc[$strCategory][$strPackage= array(
  193.                     '*docid*'   => null,
  194.                     '*package*' => $strPackageDir
  195.                 );
  196.             else {
  197.                 $arDoc[$strCategory][$strPackage=
  198.                     $this->getPackageCoverage($strPackage$strCategory$strId$strPackageDir);
  199.             }
  200.         }
  201.  
  202.         ksort($arDoc);
  203.         foreach ($arDoc as &$arPackages{
  204.             ksort($arPackages);
  205.         }
  206.  
  207.         return $arDoc;
  208.     }//public function generateCoverage()
  209.  
  210.  
  211.  
  212.     /**
  213.     *   Renders the coverage analysis into a viewable format.
  214.     *
  215.     *   @param array $arCoverage    Coverage analysis array from generateCoverage()
  216.     *   @param string $strRenderer  Classname of the renderer
  217.     *
  218.     *   @return string  Viewable coverage analysis.
  219.     */
  220.     public static function renderCoverage(
  221.         $arCoverage,
  222.         $strRenderer 'QA_Peardoc_Coverage_Renderer_SimplePackageList'
  223.     {
  224.         if (!class_exists($strRenderer)) {
  225.             require_once str_replace('_''/'$strRenderer'.php';
  226.         }
  227.  
  228.         return call_user_func(
  229.             array($strRenderer'render'),
  230.             $arCoverage
  231.         );
  232.     }//public static function renderCoverage(..)
  233.  
  234.  
  235.  
  236.     /**
  237.     *   Loads the given xml file into a DOM document.
  238.     *
  239.     *   @param string $strManualPath    Full path to the manual.xml file
  240.     *                                     of the pear documentation.
  241.     *   @return boolean true if all is ok.
  242.     */
  243.     protected function loadXmlDocument($strManualPath)
  244.     {
  245.         if (!file_exists($strManualPath)) {
  246.             throw new Exception('Manual file does not exist: ' $strManualPath);
  247.         }
  248.         $strPath getcwd();
  249.         chdir(dirname($strManualPath));
  250.  
  251.         $this->doc = new DOMDocument();
  252.         $this->doc->resolveExternals = true;
  253.         $this->doc->substituteEntities = true;
  254.         if ($this->doc->load($strManualPath)) {
  255.             $this->xpath = new DOMXPath($this->doc);
  256.             chdir($strPath);
  257.             return true;
  258.         else {
  259.             chdir($strPath);
  260.             throw new Exception('manual XML could not be loaded.');
  261.         }
  262.     }//protected function loadXmlDocument($strManualPath)
  263.  
  264.  
  265.  
  266.     /**
  267.     *   Returns a list of packages and their paths
  268.     *
  269.     *   @param string $strPearDir   PEAR CVS directory
  270.     *   @return array   Array with package dir as key and package name as value
  271.     */
  272.     public static function getPackageList($strPearDir)
  273.     {
  274.         $arPackages = array();
  275.  
  276.         if (substr($strPearDir-1!= '/'{
  277.             $strPearDir .= '/';
  278.         }
  279.         foreach (
  280.             glob($strPearDir '*/packag{e,e2}.xml'GLOB_BRACE)
  281.             as $strPackageFilePath
  282.         {
  283.             $strPackageDir substr($strPackageFilePath0strrpos($strPackageFilePath'/'));
  284.             $arPackages[$strPackageDirbasename($strPackageDir);
  285.         }
  286.  
  287.         asort($arPackages);
  288.         return $arPackages;
  289.     }//public static function getPackageList($strPearDir)
  290.  
  291.  
  292.  
  293.     /**
  294.     *   Returns the category name for the package.
  295.     *
  296.     *   @param string $strPackage   Package name (e.g. Gtk2_EntryDialog)
  297.     *   @return string              Array with category names (lowercase)
  298.     */
  299.     public static function getCategory($strPackage)
  300.     {
  301.         $strPackage strtolower($strPackage);
  302.  
  303.         if (isset(self::$arCategoryAssociation[$strPackage])) {
  304.             $strCategory = self::$arCategoryAssociation[$strPackage];
  305.         else {
  306.             $nPos = strpos($strPackage'_');
  307.             if ($nPos === false{
  308.                 $strCategory $strPackage;
  309.             else {
  310.                 $strCategory substr($strPackage0$nPos);
  311.             }
  312.         }
  313.  
  314.         if (isset(self::$arCategoryDocNames[$strCategory])) {
  315.             $arCategories = self::$arCategoryDocNames[$strCategory];
  316.         else {
  317.             $arCategories = array($strCategory);
  318.         }
  319.         if (!is_array($arCategories)) {
  320.             $arCategories = array($arCategories);
  321.         }
  322.         $arCategories array_map('strtolower'$arCategories);
  323.  
  324.         return $arCategories;
  325.     }//public static function getCategory($strPackage)
  326.  
  327.  
  328.  
  329.     /**
  330.     *   Returns the ID of the XML node of the package
  331.     *   in the pear documentation.
  332.     *
  333.     *   @param string $strPackage   Package name
  334.     *   @param string $strCategory  Category name, gotten from self::getCategory()
  335.     *
  336.     *   @return string  The id, or NULL if not found -> not documented.
  337.     */
  338.     public function getPackageDocId($strPackage$strCategory)
  339.     {
  340.         $strCategory       strtolower($strCategory);
  341.         $strPackageIdName  strtolower(str_replace('_''-'$strPackage));
  342.         $strPackageIdName2 strtolower(str_replace('_''-',
  343.                                     substr(
  344.                                         $strPackage,
  345.                                         strpos($strPackage'_'+ 1
  346.                                     )
  347.                              ));
  348.  
  349.         $strId  'package.' $strCategory '.' $strPackageIdName;
  350.         $strId2 'package.' $strCategory '.' $strPackageIdName2;
  351.         $strId3 'core.'    $strCategory '.' $strPackageIdName;
  352. //echo $strId . "|" . $strId2 . "|" . $strId3 . "\n";
  353.  
  354.         if ($this->existsId($strId)) {
  355.             return $strId;
  356.         else if ($this->existsId($strId2)) {
  357.             return $strId2;
  358.         else if ($this->existsId($strId3)) {
  359.             return $strId3;
  360.         else {
  361.             return null;
  362.         }
  363.     }//public function getPackageDocId($strPackage)
  364.  
  365.  
  366.  
  367.     /**
  368.     *   Checks if an id exists in the manual
  369.     *
  370.     *   @param string $strId    xml id="" to check
  371.     *   @return boolean     True if it exists, false if not
  372.     */
  373.     public function existsId($strId)
  374.     {
  375.         //echo $strId;
  376.         return $this->doc->getElementById($strId!= null;
  377.     }//public function existsId($strId)
  378.  
  379.  
  380.  
  381.     /**
  382.     *   Creates the documentation coverage for the given package.
  383.     *
  384.     *   A class is considered documented if either an ID
  385.     *   /baseid + "." + classname/ exists, or the classname
  386.     *   is surrounded by <classname> tags at least once.
  387.     *
  388.     *   Functions/methds are considered as documented if
  389.     *   they are mentioned inside a <function> tag or
  390.     *   an id like /baseid + "." + classname + "." + functionname/
  391.     *   exists.
  392.     *
  393.     *   @param string   $strPackage     Package name
  394.     *   @param string   $strCategory    Category (lowercase)
  395.     *   @param string   $strBaseId      Base documentation id attribute
  396.     *   @param string   $strPackageDir  Package directory
  397.     *
  398.     *   @return array   Array with doc coverage information
  399.     */
  400.     public function getPackageCoverage($strPackage$strCategory$strBaseId$strPackageDir)
  401.     {
  402.         $arDoc = array(
  403.             '*docid*'   => $strBaseId,
  404.             '*package*' => $strPackageDir
  405.         );
  406.  
  407.         $baseElement $this->doc->getElementById($strBaseId);
  408.         //find <classname> elements
  409.         $arDocClasses = array();
  410.         foreach ($baseElement->getElementsByTagName('classname'as $classElement{
  411.             $arDocClasses[$classElement->nodeValue= true;
  412.         }
  413.  
  414.         $arClasses = array();
  415.         foreach (
  416.             QA_Peardoc_Coverage_ClassList::getFileList($strPackageDir)
  417.             as $strClassFile
  418.         {
  419.             $arClasses array_merge(
  420.                 $arClasses,
  421.                 QA_Peardoc_Coverage_MethodList::getMethods($strClassFile)
  422.             );
  423.         }//foreach file
  424.  
  425.         ksort($arClasses);
  426.  
  427.         foreach ($arClasses as $strClassName => $arMethods{
  428.             if ($strClassName[0== '*'{
  429.                 continue;
  430.             }
  431.             //Check if class is documented
  432.             $strClassDocId $strBaseId '.' strtolower(str_replace('_''-'$strClassName));
  433.  
  434.             if (!isset($arDocClasses[$strClassName]&& !$this->existsId($strClassDocId)) {
  435.                 //FIXME: if classname is the same as package name -> short version
  436.                 //class is not documented
  437.                 $arDoc[$strClassName= null;
  438.                 continue;
  439.             }
  440.  
  441.             $arDocMethods = array();
  442.             foreach ($baseElement->getElementsByTagName('function'as $funcElement{
  443.                 $arDocMethods[$funcElement->nodeValue= true;
  444.             }
  445.  
  446.             //check if methods exist
  447.             foreach ($arMethods as $strMethod => $bDocBlock{
  448.                 //omit constructors
  449.                 if ($strMethod == $strClassName || $strMethod == '__construct'{
  450.                     continue;
  451.                 }
  452.  
  453.                 if (isset($arDocMethods[$strMethod])) {
  454.                     //first check if the method is in a <function> tag
  455.                     $arDoc[$strClassName][$strMethod= true;
  456.                 else {
  457.                     //then check if the method has its own section
  458.                     $strMethodDocId $strClassDocId '.' strtolower(str_replace('_''-'$strMethod));
  459.  
  460.                     $arDoc[$strClassName][$strMethod$this->existsId($strMethodDocId);
  461.                 }
  462.             }//foreach method
  463.         }//foreach class
  464.  
  465.         return $arDoc;
  466.     }//public function getPackageCoverage($strPackage, $strCategory, $strBaseId, $strPackageDir)
  467.  
  468. }//class QA_Peardoc_Coverage
  469. ?>

Documentation generated on Mon, 11 Mar 2019 14:48:03 -0400 by phpDocumentor 1.4.4. PEAR Logo Copyright © PHP Group 2004.