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'                    => 'php',
  77.         'phpdocumentor'             => 'php',
  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.         $arDoc['*date*'time();
  208.  
  209.         return $arDoc;
  210.     }//public function generateCoverage()
  211.  
  212.  
  213.  
  214.     /**
  215.     *   Renders the coverage analysis into a viewable format.
  216.     *
  217.     *   @param array $arCoverage    Coverage analysis array from generateCoverage()
  218.     *   @param string $strRenderer  Classname of the renderer
  219.     *
  220.     *   @return string  Viewable coverage analysis.
  221.     */
  222.     public static function renderCoverage(
  223.         $arCoverage,
  224.         $strRenderer 'QA_Peardoc_Coverage_Renderer_SimplePackageList'
  225.     {
  226.         if (!class_exists($strRenderer)) {
  227.             require_once str_replace('_''/'$strRenderer'.php';
  228.         }
  229.  
  230.         return call_user_func(
  231.             array($strRenderer'render'),
  232.             $arCoverage
  233.         );
  234.     }//public static function renderCoverage(..)
  235.  
  236.  
  237.  
  238.     /**
  239.     *   Loads the given xml file into a DOM document.
  240.     *
  241.     *   @param string $strManualPath    Full path to the manual.xml file
  242.     *                                     of the pear documentation.
  243.     *   @return boolean true if all is ok.
  244.     */
  245.     protected function loadXmlDocument($strManualPath)
  246.     {
  247.         if (!file_exists($strManualPath)) {
  248.             throw new Exception('Manual file does not exist: ' $strManualPath);
  249.         }
  250.         $strPath getcwd();
  251.         chdir(dirname($strManualPath));
  252.  
  253.         $this->doc = new DOMDocument();
  254.         $this->doc->resolveExternals = true;
  255.         $this->doc->substituteEntities = true;
  256.         if ($this->doc->load($strManualPath)) {
  257.             $this->xpath = new DOMXPath($this->doc);
  258.             chdir($strPath);
  259.             return true;
  260.         else {
  261.             chdir($strPath);
  262.             throw new Exception('manual XML could not be loaded.');
  263.         }
  264.     }//protected function loadXmlDocument($strManualPath)
  265.  
  266.  
  267.  
  268.     /**
  269.     *   Returns a list of packages and their paths
  270.     *
  271.     *   @param string $strPearDir   PEAR CVS directory
  272.     *   @return array   Array with package dir as key and package name as value
  273.     */
  274.     public static function getPackageList($strPearDir)
  275.     {
  276.         $arPackages = array();
  277.  
  278.         if (substr($strPearDir-1!= '/'{
  279.             $strPearDir .= '/';
  280.         }
  281.         foreach (
  282.             glob($strPearDir '*/packag{e,e2}.xml'GLOB_BRACE)
  283.             as $strPackageFilePath
  284.         {
  285.             $strPackageDir substr($strPackageFilePath0strrpos($strPackageFilePath'/'));
  286.             $arPackages[$strPackageDirbasename($strPackageDir);
  287.         }
  288.  
  289.         asort($arPackages);
  290.         return $arPackages;
  291.     }//public static function getPackageList($strPearDir)
  292.  
  293.  
  294.  
  295.     /**
  296.     *   Returns the category name for the package.
  297.     *
  298.     *   @param string $strPackage   Package name (e.g. Gtk2_EntryDialog)
  299.     *   @return string              Array with category names (lowercase)
  300.     */
  301.     public static function getCategory($strPackage)
  302.     {
  303.         $strPackage strtolower($strPackage);
  304.  
  305.         if (isset(self::$arCategoryAssociation[$strPackage])) {
  306.             $strCategory = self::$arCategoryAssociation[$strPackage];
  307.         else {
  308.             $nPos = strpos($strPackage'_');
  309.             if ($nPos === false{
  310.                 $strCategory $strPackage;
  311.             else {
  312.                 $strCategory substr($strPackage0$nPos);
  313.             }
  314.         }
  315.  
  316.         if (isset(self::$arCategoryDocNames[$strCategory])) {
  317.             $arCategories = self::$arCategoryDocNames[$strCategory];
  318.         else {
  319.             $arCategories = array($strCategory);
  320.         }
  321.         if (!is_array($arCategories)) {
  322.             $arCategories = array($arCategories);
  323.         }
  324.         $arCategories array_map('strtolower'$arCategories);
  325.  
  326.         return $arCategories;
  327.     }//public static function getCategory($strPackage)
  328.  
  329.  
  330.  
  331.     /**
  332.     *   Returns the ID of the XML node of the package
  333.     *   in the pear documentation.
  334.     *
  335.     *   @param string $strPackage   Package name
  336.     *   @param string $strCategory  Category name, gotten from self::getCategory()
  337.     *
  338.     *   @return string  The id, or NULL if not found -> not documented.
  339.     */
  340.     public function getPackageDocId($strPackage$strCategory)
  341.     {
  342.         $strCategory       strtolower($strCategory);
  343.         $strPackageIdName  strtolower(str_replace('_''-'$strPackage));
  344.         $strPackageIdName2 strtolower(str_replace('_''-',
  345.                                     substr(
  346.                                         $strPackage,
  347.                                         strpos($strPackage'_'+ 1
  348.                                     )
  349.                              ));
  350.  
  351.         $strId  'package.' $strCategory '.' $strPackageIdName;
  352.         $strId2 'package.' $strCategory '.' $strPackageIdName2;
  353.         $strId3 'core.'    $strCategory '.' $strPackageIdName;
  354. //echo $strId . "|" . $strId2 . "|" . $strId3 . "\n";
  355.  
  356.         if ($this->existsId($strId)) {
  357.             return $strId;
  358.         else if ($this->existsId($strId2)) {
  359.             return $strId2;
  360.         else if ($this->existsId($strId3)) {
  361.             return $strId3;
  362.         else {
  363.             return null;
  364.         }
  365.     }//public function getPackageDocId($strPackage)
  366.  
  367.  
  368.  
  369.     /**
  370.     *   Checks if an id exists in the manual
  371.     *
  372.     *   @param string $strId    xml id="" to check
  373.     *   @return boolean     True if it exists, false if not
  374.     */
  375.     public function existsId($strId)
  376.     {
  377.         //echo $strId;
  378.         return $this->doc->getElementById($strId!= null;
  379.     }//public function existsId($strId)
  380.  
  381.  
  382.  
  383.     /**
  384.     *   Creates the documentation coverage for the given package.
  385.     *
  386.     *   A class is considered documented if either an ID
  387.     *   /baseid + "." + classname/ exists, or the classname
  388.     *   is surrounded by <classname> tags at least once.
  389.     *
  390.     *   Functions/methds are considered as documented if
  391.     *   they are mentioned inside a <function> tag or
  392.     *   an id like /baseid + "." + classname + "." + functionname/
  393.     *   exists.
  394.     *
  395.     *   @param string   $strPackage     Package name
  396.     *   @param string   $strCategory    Category (lowercase)
  397.     *   @param string   $strBaseId      Base documentation id attribute
  398.     *   @param string   $strPackageDir  Package directory
  399.     *
  400.     *   @return array   Array with doc coverage information
  401.     */
  402.     public function getPackageCoverage($strPackage$strCategory$strBaseId$strPackageDir)
  403.     {
  404.         $arDoc = array(
  405.             '*docid*'   => $strBaseId,
  406.             '*package*' => $strPackageDir
  407.         );
  408.  
  409.         $baseElement $this->doc->getElementById($strBaseId);
  410.         //find <classname> elements
  411.         $arDocClasses = array();
  412.         foreach ($baseElement->getElementsByTagName('classname'as $classElement{
  413.             $arDocClasses[$classElement->nodeValue= true;
  414.         }
  415.  
  416.         $arClasses = array();
  417.         foreach (
  418.             QA_Peardoc_Coverage_ClassList::getFileList($strPackageDir)
  419.             as $strClassFile
  420.         {
  421.             $arClasses array_merge(
  422.                 $arClasses,
  423.                 QA_Peardoc_Coverage_MethodList::getMethods($strClassFile)
  424.             );
  425.         }//foreach file
  426.  
  427.         ksort($arClasses);
  428.  
  429.         foreach ($arClasses as $strClassName => $arMethods{
  430.             if ($strClassName[0== '*'{
  431.                 continue;
  432.             }
  433.             //Check if class is documented
  434.             $strClassDocId $strBaseId '.' strtolower(str_replace('_''-'$strClassName));
  435.  
  436.             if (!isset($arDocClasses[$strClassName]&& !$this->existsId($strClassDocId)) {
  437.                 //class is not documented
  438.                 $arDoc[$strClassName= null;
  439.                 continue;
  440.             }
  441.  
  442.             $arDocMethods = array();
  443.             foreach ($baseElement->getElementsByTagName('function'as $funcElement{
  444.                 $arDocMethods[$funcElement->nodeValue= true;
  445.             }
  446.  
  447.             $arDoc[$strClassName= array();
  448.  
  449.             //check if methods exist
  450.             foreach ($arMethods as $strMethod => $bDocBlock{
  451.                 //omit constructors
  452.                 if ($strMethod == $strClassName || $strMethod == '__construct'{
  453.                     continue;
  454.                 }
  455.  
  456.                 if (isset($arDocMethods[$strMethod])) {
  457.                     //first check if the method is in a <function> tag
  458.                     $arDoc[$strClassName][$strMethod= true;
  459.                 else {
  460.                     //then check if the method has its own section
  461.                     $strMethodDocId $strClassDocId '.' strtolower(str_replace('_''-'$strMethod));
  462.  
  463.                     $arDoc[$strClassName][$strMethod$this->existsId($strMethodDocId);
  464.                 }
  465.             }//foreach method
  466.         }//foreach class
  467.  
  468.         return $arDoc;
  469.     }//public function getPackageCoverage($strPackage, $strCategory, $strBaseId, $strPackageDir)
  470.  
  471. }//class QA_Peardoc_Coverage
  472. ?>

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