#!/bin/bash

##########
# Git Pre-Commit file for PHP projects
###
#
# This hook performs the following validation:
#   - PHP CodeSniffer
#   - PHP Mess Detector
#
# Based on https://gist.github.com/codemis/8225337
#
# @author Davi Marcondes Moreira <davi.marcondes.moreira@gmail.com>
#
##########


##########
# DEFAULT SETTINGS
###
#
# These variables define the basic values for Code_Sniffer and PHPMD.
# Override these by creating a new variable on the `config` file.
#
##########
PHPCS_ACTIVE=1
PHPCS_BIN=/usr/bin/phpcs
PHPCS_CODING_STANDARD=PEAR
PHPCS_IGNORE=
PHPMD_ACTIVE=1
PHPMD_BIN=/usr/bin/phpmd
PHPMD_OUTPUT=text
PHPMD_PATTERNS_LIST=cleancode,codesize,controversial,design,naming,unusedcode
TMP_STAGING="/tmp/.tmp_staging"


##########
# Parse config file.
##########
CONFIG_FILE=$(dirname $0)/config
if [ -e $CONFIG_FILE ]; then
    . $CONFIG_FILE
fi


##########
# First: check if PHP Code_Sniffer and PHPMD bin files are present && executable.
##########
if [ ! -x $PHPCS_BIN ] || [ ! -x $PHPMD_BIN ]; then
    tput setaf 1; echo "Executable not found. Check $PHPCS_BIN and $PHPMD_BIN."
    exit 1
fi


##########
# Git Check-up
##########
if git rev-parse --verify HEAD
then
    against=HEAD
else
    # Initial commit: diff against an empty tree object
    against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

# This is the magic:
# Retrieve all files in staging area that are ADDED, MODIFIED or RENAMED,
# but no deletions etc.
# Lets first check if there are any file pattern to exclude from this list.
if [ "$GIT_EXCLUDE" != "" ]; then
    GIT_EXCLUDE_LIST="| grep -v $GIT_EXCLUDE"
else
    GIT_EXCLUDE_LIST=""
fi


FILES=$(git diff-index --name-only --cached --diff-filter=ACMR $against -- $GIT_EXCLUDE_LIST)

if [ "$FILES" == "" ]; then
    exit 0
fi


# Create temporary copy of staging area
if [ -e $TMP_STAGING ]; then
    rm -rf $TMP_STAGING
fi
mkdir $TMP_STAGING

# Match files against whitelist
FILES_TO_CHECK=""
for FILE in $FILES
do
    echo "$FILE" | egrep -q "$PHPCS_FILE_PATTERN"
    RETVAL=$?
    if [ "$RETVAL" -eq "0" ]
    then
        FILES_TO_CHECK="$FILES_TO_CHECK $FILE"
    fi
done

if [ "$FILES_TO_CHECK" == "" ]; then
    exit 0
fi


##########
# Validate PHP CodeSniffer variables
##########
if [ "$PHPCS_ACTIVE" != "1" ]; then
    PHPCS_ACTIVE=0
fi

if [ "$PHPCS_IGNORE" != "" ]; then
    IGNORE="--ignore=$PHPCS_IGNORE"
else
    IGNORE=""
fi

if [ "$PHPCS_CODING_STANDARD" != "" ]; then
    PHPCS_CODING_STANDARD="--standard=$PHPCS_CODING_STANDARD"
else
    PHPCS_CODING_STANDARD=""
fi

if [ "$PHPCS_SNIFFS" != "" ]; then
    SNIFFS="--sniffs=$PHPCS_SNIFFS"
else
    SNIFFS=""
fi

if [ "$PHPCS_ENCODING" != "" ]; then
    ENCODING="--encoding=$PHPCS_ENCODING"
else
    ENCODING=""
fi

if [ "$PHPCS_IGNORE_WARNINGS" == "1" ]; then
    IGNORE_WARNINGS="-n"
else
    IGNORE_WARNINGS=""
fi


##########
# Validate PHP Mess Detector variables
##########
if [ "$PHPMD_ACTIVE" != "1" ]; then
    PHPMD_ACTIVE=0
fi

if [ "$PHPMD_OUTPUT_MODE" != "" ]; then
    PHPMD_OUTPUT="$PHPMD_OUTPUT_MODE"
else
    PHPMD_OUTPUT="text"
fi

if [ "$PHPMD_PATTERNS" != "" ]; then
    PHPMD_PATTERNS_LIST="$PHPMD_PATTERNS"
else
    PHPMD_PATTERNS_LIST="cleancode"
fi

if [ "$PHPMD_SUFFIXES" != "" ]; then
    PHPMD_SUFFIXES_LIST="--suffixes $PHPMD_SUFFIXES"
else
    PHPMD_SUFFIXES_LIST=""
fi

if [ "$PHPMD_EXCLUDE" != "" ]; then
    PHPMD_EXCLUDE_LIST="--exclude $PHPMD_EXCLUDE"
else
    PHPMD_EXCLUDE_LIST=""
fi


##########
# Copy contents of staged version of files to temporary staging area
# because we only want the staged version that will be commited and not
# the version in the working directory.
##########
STAGED_FILES=""
for FILE in $FILES_TO_CHECK
do
    ID=$(git diff-index --cached $against $FILE | cut -d " " -f4)
    ##########
    # Create staged version of file in temporary staging area with the same
    # path as the original file so that the phpcs ignore filters can be applied.
    ##########
    mkdir -p "$TMP_STAGING/$(dirname $FILE)"
    git cat-file blob $ID > "$TMP_STAGING/$FILE"
    STAGED_FILES="$STAGED_FILES $TMP_STAGING/$FILE"
done


##########
# CODE INSPECTION: PHP CodeSniffer
##########
if [ "$PHPCS_ACTIVE" == "1" ]; then
    echo ""
    tput setaf 12; echo " :: PHP CodeSniffer inspection :: "
    PHPCS_OUTPUT=$($PHPCS_BIN -s $IGNORE_WARNINGS $PHPCS_CODING_STANDARD $ENCODING $IGNORE $STAGED_FILES)
    PHPCS_RETVAL=$?

    if [ $PHPCS_RETVAL -ne 0 ]; then
        tput setaf 1; echo " ✘ Issues found: "
        tput setaf 7; echo "$PHPCS_OUTPUT"

        rm -rf $TMP_STAGING

        exit $PHPCS_RETVAL
    else
        tput setaf 2; echo " ✔ Inspection is OK!"
    fi
else
    echo ""
    tput setaf 8; echo " ➔ PHP CodeSniffer inspection is OFF."
fi


##########
# CODE INSPECTION: PHP Mess Detector
##########
if [ "$PHPMD_ACTIVE" == "1" ]; then
    echo ""
    tput setaf 12; echo " :: PHP Mess Detector inspection :: "
    PHPMD_OUTPUT=$($PHPMD_BIN $STAGED_FILES $PHPMD_OUTPUT $PHPMD_PATTERNS_LIST $PHPMD_SUFFIXES_LIST $PHPMD_EXCLUDE_LIST)
    PHPMD_RETVAL=$?

    if [ $PHPMD_RETVAL -ne 0 ]; then
        tput setaf 1; echo " ✘ Issues found: "
        tput setaf 7; echo "$PHPMD_OUTPUT"

        rm -rf $TMP_STAGING

        exit $PHPMD_RETVAL
    else
        tput setaf 2; echo " ✔ Inspection is OK!"
    fi
else
    echo ""
    tput setaf 8; echo " ➔ PHP Mess Detector inspection is OFF."
fi

tput setaf 12;



rm -rf $TMP_STAGING



echo ""
exit 0;