Monday, December 27, 2010

Converting PHP_CodeSniffer XML report into HTML page (XSLT)

PHP CodeSniffer XML report will look similar to this:
<?xml version="1.0" encoding="UTF-8"?>
<phpcs version="1.2.2">
 <file name="/home/myhome/NetBeansProjects/LazyLoad/Libs/LazyLoad/Repositories/NestedSetNode.php" errors="7" warnings="1">
  <error line="39" column="11" source="Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase">Constants must be uppercase; expected LAZYLOAD but found LazyLoad</error>
  <error line="39" column="20" source="Generic.NamingConventions.UpperCaseConstantName.ConstantNotUpperCase">Constants must be uppercase; expected REPOSITORIES but found Repositories</error>
  <error line="58" column="1" source="PEAR.Commenting.ClassComment">Missing @link tag in class comment</error>
  <error line="69" column="13" source="PEAR.NamingConventions.ValidVariableName">Private member variable &quot;set&quot; must be prefixed with an underscore</error>
  <error line="75" column="13" source="PEAR.NamingConventions.ValidVariableName">Private member variable &quot;lft&quot; must be prefixed with an underscore</error>
  <error line="81" column="13" source="PEAR.NamingConventions.ValidVariableName">Private member variable &quot;rgt&quot; must be prefixed with an underscore</error>
  <warning line="148" column="9" source="PEAR.ControlStructures.InlineControlStructure">Inline control structures are discouraged</warning>
 </file>
</phpcs>
To transform PHP CodeSniffer XML report into human readable format XSLT stylesheet needs to be attached:
<?xml version="1.0" encoding="UTF-8"?>
<!-- add line -->
<?xml-stylesheet type="text/xsl" href="phpcs.xsl"?>
<!-- rest of PHP CodeSniffer XML report -->
XSLT stylesheet:
<?xml version="1.0" encoding="UTF-8"?>

<!--
    Document   : phpcs.xsl
    Created on : December 27, 2010, 1:42 PM
    Author     : schkovich
    Description:
        Transformation PHP_CodeSniffer xml report into human readable format.
-->

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
    <xsl:output method="html"  encoding="UTF-8"/>

    <!-- TODO customize transformation rules
         syntax recommendation http://www.w3.org/TR/xslt
    -->
    <xsl:template match="/">
        <html>
            <head>
                <title>phpcs.xsl</title>
                <link href="./phpcs.css" rel="stylesheet" type="text/css" />
            </head>
            <body>
                <table>
                    <thead>
                        <tr>
                            <th class="file">Name</th>
                            <th class="notes">Errors</th>
                            <th class="notes">Warnings</th>
                        </tr>
                    </thead>
                    <tbody>
                        <xsl:for-each select="phpcs/file">
                            <tr>
                                <td>
                                    <xsl:value-of select="@name" />
                                </td>
                                <td>
                                    <xsl:value-of select="@errors" />
                                </td>
                                <td>
                                    <xsl:value-of select="@warnings" />
                                </td>
                            </tr>
                            <tr>
                                <td colspan="3">
                                    <xsl:for-each select="error">
                                        <span class="error">Error: </span>
                                        <xsl:value-of select="self::node()"/>
                                        <br />
                                        <b>Line:</b>
                                        <xsl:value-of select="@line" />
                                        <br />
                                        <b>Column:</b>
                                        <xsl:value-of select="@column" />
                                        <br />
                                        <b>Source:</b>
                                        <xsl:value-of select="@source" />
                                        <hr />
                                    </xsl:for-each>
                                    <xsl:for-each select="warning">
                                        <span class="warning">Warning: </span>
                                        <xsl:value-of select="self::node()"/>
                                        <br />
                                        <b>Line:</b>
                                        <xsl:value-of select="@line" />
                                        <br />
                                        <b>Column:</b>
                                        <xsl:value-of select="@column" />
                                        <br />
                                        <b>Source:</b>
                                        <xsl:value-of select="@source" />
                                        <hr />
                                    </xsl:for-each>
                                </td>
                            </tr>
                        </xsl:for-each>
                    </tbody>
                </table>
            </body>
        </html>
    </xsl:template>

</xsl:stylesheet>
CSS
/*
    Document   : phpcs
    Created on : Dec 27, 2010, 1:34:02 PM
    Author     : schkovich
    Description:
        This stylesheet is designed to style phpcs XSLT stylessheet
*/

/*
   TODO customize this sample style
   Syntax recommendation http://www.w3.org/TR/REC-CSS2/
*/
table {
    width: 100%
}
th {
    text-align: left
}
th.file {
    width: 80%;
    color: green
}
th.notes {
    width: 10%;
    color: blue
}
span {
    font-weight: bold
}
span.error {
    color: red
}
span.warning {
    color: orange
}
Output will look like this:
To generate PHP CodeSniffer reports that will always have attached external XSLT stylesheet one PHP CodeSniffer file needs to be patched and one needs to be added.
New code generator CodeSniffer/Reports.Xls.php needs to be added:
<?php
/**
 * Xsl report for PHP_CodeSniffer.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Gabriele Santini <gsantini@sqli.com>
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Goran Miskovic <schkovich@gmail.com>
 * @copyright 2009 SQLI <www.sqli.com>
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   CVS: $Id: IsCamelCapsTest.php 240585 2007-08-02 00:05:40Z squiz $
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */

/**
 * Xml report for PHP_CodeSniffer.
 *
 * PHP version 5
 *
 * @category  PHP
 * @package   PHP_CodeSniffer
 * @author    Gabriele Santini <gsantini@sqli.com>
 * @author    Greg Sherwood <gsherwood@squiz.net>
 * @author    Goran Miskovic <schkovich@gmail.com>
 * @copyright 2009 SQLI <www.sqli.com>
 * @copyright 2006 Squiz Pty Ltd (ABN 77 084 670 600)
 * @license   http://matrix.squiz.net/developer/tools/php_cs/licence BSD Licence
 * @version   Release: 1.2.2
 * @link      http://pear.php.net/package/PHP_CodeSniffer
 */
class PHP_CodeSniffer_Reports_Xsl implements PHP_CodeSniffer_Report
{


    /**
     * Prints all violations for processed files, in a proprietary XML format.
     *
     * Errors and warnings are displayed together, grouped by file.
     * 
     * External XSLT stylesheet phpcs.xsl will be attached
     *
     * @param array   $report       Prepared report.
     * @param boolean $showWarnings Show warnings?
     * @param boolean $showSources  Show sources?
     * @param int     $width        Maximum allowed lne width.
     * 
     * @return string 
     */
    public function generate(
        $report,
        $showWarnings=true,
        $showSources=false,
        $width=80
    ) {
        echo '<?xml version="1.0" encoding="UTF-8"?>'.PHP_EOL;
        echo '<?xml-stylesheet type="text/xsl" href="phpcs.xsl"?>'.PHP_EOL;
        echo '<phpcs version="1.2.2">'.PHP_EOL;

        $errorsShown = 0;

        foreach ($report['files'] as $filename => $file) {
            if (empty($file['messages']) === true) {
                continue;
            }

            echo ' <file name="'.$filename.'" errors="'.$file['errors'].'" warnings="'.$file['warnings'].'">'.PHP_EOL;

            foreach ($file['messages'] as $line => $lineErrors) {
                foreach ($lineErrors as $column => $colErrors) {
                    foreach ($colErrors as $error) {
                        $error['type'] = strtolower($error['type']);
                        echo '  <'.$error['type'].' line="'.$line.'" column="'.$column.'" source="'.$error['source'].'">';
                        echo htmlspecialchars($error['message']).'</'.$error['type'].'>'.PHP_EOL;
                        $errorsShown++;
                    }
                }
            }//end foreach

            echo ' </file>'.PHP_EOL;
        }//end foreach

        echo '</phpcs>'.PHP_EOL;

        return $errorsShown;

    }//end generate()


}//end class

?>
CodeSniffer/CLI.php needs to be patched. Find variable $validReports on line 262 and add 'xsl' as the last array element:
$validReports     = array(
                                     'full',
                                     'xml',
                                     'checkstyle',
                                     'csv',
                                     'emacs',
                                     'source',
                                     'summary',
                                     'svnblame',
                                     'xsl',
                                    );
Finally run from the command line:
phpcs --report=xsl --report-file=${HOME}/NetBeansProjects/LazyLoad/public_html/LazyLoad.xml NetBeansProjects/LazyLoad/Libs

3 comments:

  1. Hi Goran

    Is this method correct for new versions of phpcs? here is the error

    Surens-MacBook-Pro:website Mac$ phpcs --report=xsl --report-file=build/logs/phpcs.xsl --tab-width=4 website/apps/actions.class.php
    PHP Fatal error: Uncaught exception 'PHP_CodeSniffer_Exception' with message 'Report type "Xsl" not found.' in /Users/Mac/pear/share/pear/PHP/CodeSniffer/Reporting.php:93
    Stack trace:
    #0 /Users/Mac/pear/share/pear/PHP/CodeSniffer/Reporting.php(127): PHP_CodeSniffer_Reporting->factory('xsl')
    #1 /Users/Mac/pear/share/pear/PHP/CodeSniffer.php(1380): PHP_CodeSniffer_Reporting->cacheFileReport(Object(PHP_CodeSniffer_File), Array)

    ReplyDelete