Measuring PHP code complexity

What makes good code and why is some code better than others? One quantitative approach to answer this is the use of software metrics. These metrics try to capture the size and complexity of code in numbers (e.g. ‘lines of code‘, ‘cyclomatic complexity‘) and can be useful indicators for maintainability and simplicity (or more often the lack thereof).

I found three ways to get complexity measures for PHP code.

The most advanced technique is to use a continuous integration server. One good example is the server at test.pear.php.net which uses CruiseControl (a Java CI server) with phpUnderControl for the PHP specific parts. Certainly a nice solution for big projects, but nothing to use on your own laptop to improve a single package.

Often the easiest way is to use PHP_CodeSniffer, which includes checks for cyclomatic complexity and nesting level (causing warnings and errors above some thresholds). If you already use it to maintain a uniform coding style (e.g. PEAR, Zend) then you just have to add these checks to your standard definition. These commands add metrics to the PEAR standard:

cd /usr/local/share/pear/PHP/CodeSniffer/Standards
sudo cp -R Generic/Sniffs/Metrics PEAR/Sniffs
sudo sed -I .bck -e 's/Squiz_/PEAR_/' PEAR/Sniffs/Metrics/*

The program PDepend creates a more detailed analysis of methods, classes, and projects (see this release note for details). Because it is intended as a component within a CI server its primary output is in XML. So I wrote myself a small wrapper (pdepend_summary.php) to format the data as an ASCII table. This allows for quick edit-check-cycles and now I run it along with phpcs and phpunit before I commit PEAR code.

Example output:

Package              Class                                  LoC %Comment Variables Methods Class_SiZe   WMC
-------              -----                                  --- -------- --------- ------- ----------   ---
Payment_DTA          DTA                                   1084     36.3         3      15        114   111
Payment_DTA          DTAZV                                  757     37.1         1      12         72    71
Payment_DTA          DTABase                                563     33.0         6      19         33    27
Payment_DTA          Payment_DTA_ChecksumException            3      0.0         0       0          0     0
Payment_DTA          Payment_DTA_FatalParseException          3      0.0         0       0          0     0
Payment_DTA          Payment_DTA_Exception                    3      0.0         0       0          0     0
Payment_DTA          Payment_DTA_ParseException               3      0.0         0       0          0     0
 
Package/Class             Method                                   LoC %Comment    CCN    NPath
-------------             ------                                   --- --------    ---    -----
Payment_DTA/DTA           _generateCrecord                         158     25.9     13     1600
Payment_DTA/DTA           _parseCextension                          81     30.9     13       93
Payment_DTA/DTA           _processCextension                        47      2.1      8        8
Payment_DTA/DTA           parse                                     59     35.6      7       36
Payment_DTA/DTAZV         _exchangeFillSender                       33      0.0      7       64
Payment_DTA/DTAZV         parse                                     41     17.1      7       36
Payment_DTA/DTAZV         _exchangeFillReceiver                     26      7.7      6       24
Payment_DTA/DTA           _exchangeFillArrays                       31      0.0      6       32
Payment_DTA/DTA           _parseCrecord                            109     27.5      6       72
Payment_DTA/DTAZV         setAccountFileSender                      41      0.0      5       15
Payment_DTA/DTA           _parseErecord                             49     30.6      5       16
Payment_DTA/DTABase       getStr                                    18      0.0      4        8
Payment_DTA/DTAZV         __construct                               19     10.5      4        4
...

Comments are closed.