1. Installing prerequisites
    1. Homebrew
    2. Install PHP using Homebrew
    3. Install PHP Composer
  2. Installing the tools
    1. Detecting Copy-Pastes (duplicate code)
    2. Getting code quality metrics (Generic)
    3. Getting code quality metrics (OOP)
  3. Putting it all together
  4. Results
    1. Output folder and file structure
    2. Copy-paste detection
    3. Code-related statistics

While it does not replace good judgement and programming practice completely, it can be interesting to perform regular code quality analyses to make sure that your teams (and students) are writing good PHP code. This is a small guide on how to install them in macOS and how to use them to automatically produce metrics for all code repositories in a folder.

This is intended as a very short guide on how to perform static code analysis on projects that are not under Git source code control, with minimal configuration and without relying on a CI/CD solution. For those other cases, I recommend Codacy and Travis.CI. I have been using these myself for years and I think they are great!

Installing prerequisites

Before installing the tools themselves, you may have to install some supporting software in your Mac. If you already have these installed, please move on to the tool installation section.

Homebrew

First install Homebrew, the missing package manager for macOS.

1
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Install PHP using Homebrew

Here I am installing PHP 7.2, which is the same version used at FEUP servers. Changing the PHP version may be necessary as some of the tools may throw fatal errors if you are running an old version of PHP (like I was before I forced the system to use 7.2).

1
brew install php@7.2

To use this version of PHP instead of whatever you have installed in your system (5.3 on Sierra, which is very old but what I have), you need to add the following to your ~/.zshrc file (if you use zsh) or ~/.bash_profile (if you use bash).

1
2
export PATH="/usr/local/opt/php@7.2/bin:$PATH"
export PATH="/usr/local/opt/php@7.2/sbin:$PATH"

Check your PHP version:

1
2
3
4
5
6
joaorocha at modo in ~
$ php --version
PHP 7.2.26 (cli) (built: Jan  3 2020 18:47:49) ( NTS )
Copyright (c) 1997-2018 The PHP Group
Zend Engine v3.2.0, Copyright (c) 1998-2018 Zend Technologies
    with Zend OPcache v7.2.26, Copyright (c) 1999-2018, by Zend Technologies

Install PHP Composer

1
brew install composer

Check your Composer version:

1
2
3
joaorocha at modo in ~
$ composer --version
Composer version 1.9.1 2019-11-01 17:20:17

Installing the tools

We are going to target 3 types of analyses: Detection of duplicates, size and complexity of code without considering OOP metrics, and detailed analysis of OOP structure in PHP.

Detecting Copy-Pastes (duplicate code)

An interesting tool to detect copy pasted code is PHP Copy/Paste Detector (PHPCPD).

Let’s install it:

1
composer global require sebastian/phpcpd

And run it:

1
phpcpd --fuzzy /folder/you/want/to/analyse

Getting code quality metrics (Generic)

phploc is a tool for quickly measuring the size and analyzing the structure of a PHP project.

Let’s install it:

1
composer global require phploc/phploc

And run it:

1
phploc /path/to/your/sources

Getting code quality metrics (OOP)

Another interesting open-source tool is PHPMetrics. It produces some nice HTML reports, with support for color blind people too!

According to the authors, PhpMetrics is intended for projects with usages of packages, namespaces, classes, interfaces, and other structures like that..

Let’s install it:

1
composer global require 'phpmetrics/phpmetrics'

The composer executables may not be added to your path by default. Let’s fix that by adding the following line to your ~/.zshrc file (if you use zsh) or ~/.bash_profile (if you use bash):

1
export PATH=$PATH:~/.composer/vendor/bin

And run the command:

1
phpmetrics --report-html=myreport.html /path/of/your/sources

Putting it all together

Here is a script to perform all the analyses shown before, and place the results in a neat folder called output beside your source folders. For every folder, the script will run these utilities and produce a subfolder with the results of every single one.

Create a new file called evaluate.sh in the folder that contains the subfolders that you want to analyse and copy and paste the following lines.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#!/usr/bin/env bash

NUMBER_OF_LINES_CONSIDERED_COPY_PASTE=2
MIN_TOKENS_CONSIDERED_COPY_PASTE=15
OUTPUT_DIR=$(pwd)/output

# exclude hidden directories
shopt -s dotglob

# delete existing results if they exist
if [[ -d "$OUTPUT_DIR" ]]; then
	rm -rf "$OUTPUT_DIR"
fi

# for every folder
for subfolder in */ ; do
	echo "####################################################"
	echo "Analysing $subfolder ..."
	echo "####################################################"
	SUBFOLDER_NAME=$(basename "$subfolder")
	RESULTS_SUBDIR="$OUTPUT_DIR/$SUBFOLDER_NAME"
	if [[ ! -d "$RESULTS_SUBDIR" ]]; then
		mkdir -p "$RESULTS_SUBDIR"
	fi
	
	# run phpcpd
	phpcpd --min-tokens="$MIN_TOKENS_CONSIDERED_COPY_PASTE" --min-lines="$NUMBER_OF_LINES_CONSIDERED_COPY_PASTE" --fuzzy "$subfolder" > "$RESULTS_SUBDIR/phpcpd.txt"
	
	# run phploc
	phploc "$subfolder" > "$RESULTS_SUBDIR/phploc.txt"
	
	#run phpmetrics
	phpmetrics --report-html="$RESULTS_SUBDIR/phpmetrics" "$subfolder"
done

To get the results, cd to the previously mentioned folder, and make the script executable with chmod +x evaluate.sh and run it using ./evaluate.sh.

Results

You will get something like this.

Output folder and file structure

PHP Static Analysis Results - File and folder structure

Copy-paste detection

phpcpd 4.1.0 by Sebastian Bergmann.

No clones found.

Time: 35 ms, Memory: 4.00MB
phploc 5.0.0 by Sebastian Bergmann.

Size
  Lines of Code (LOC)                             1948
  Comment Lines of Code (CLOC)                      43 (2.21%)
  Non-Comment Lines of Code (NCLOC)               1905 (97.79%)
  Logical Lines of Code (LLOC)                     627 (32.19%)
    Classes                                          0 (0.00%)
      Average Class Length                           0
        Minimum Class Length                         0
        Maximum Class Length                         0
      Average Method Length                          0
        Minimum Method Length                        0
        Maximum Method Length                        0
    Functions                                      318 (50.72%)
      Average Function Length                        6
    Not in classes or functions                    309 (49.28%)

Cyclomatic Complexity
  Average Complexity per LLOC                     0.22
  Average Complexity per Class                    0.00
    Minimum Class Complexity                      0.00
    Maximum Class Complexity                      0.00
  Average Complexity per Method                   0.00
    Minimum Method Complexity                     0.00
    Maximum Method Complexity                     0.00

Dependencies
  Global Accesses                                  166
    Global Constants                                 0 (0.00%)
    Global Variables                                50 (30.12%)
    Super-Global Variables                         116 (69.88%)
  Attribute Accesses                                 0
    Non-Static                                       0 (0.00%)
    Static                                           0 (0.00%)
  Method Calls                                     191
    Non-Static                                     191 (100.00%)
    Static                                           0 (0.00%)

Structure
  Namespaces                                         0
  Interfaces                                         0
  Traits                                             0
  Classes                                            0
    Abstract Classes                                 0 (0.00%)
    Concrete Classes                                 0 (0.00%)
  Methods                                            0
    Scope
      Non-Static Methods                             0 (0.00%)
      Static Methods                                 0 (0.00%)
    Visibility
      Public Methods                                 0 (0.00%)
      Non-Public Methods                             0 (0.00%)
  Functions                                         53
    Named Functions                                 53 (100.00%)
    Anonymous Functions                              0 (0.00%)
  Constants                                          0
    Global Constants                                 0 (0.00%)
    Class Constants                                  0 (0.00%)