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

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