Potherca
3/6/2018 - 3:34 PM

AVNLIPPP -- Average variable name length in popular PHP projects

AVNLIPPP -- Average variable name length in popular PHP projects

#!/usr/bin/env bash

set -o errexit  # Exit script when a command exits with non-zero status.
set -o errtrace # Exit on error inside any functions or sub-shells.
set -o nounset  # Exit script on use of an undefined variable.
set -o pipefail # Return exit status of the last command in the pipe that exited with a non-zero exit code

: readonly "${GRAPH_URL:=https://image-charts.com/chart?chs=800x600&cht=bvs&chds=a&chxt=x,y&chg=1,1&chco=777BB3&chtt=%s+PHP+variable+name+length&chd=t:0,%s}"
# : readonly "${NORMAL_OUTPUT:=/dev/stdout}"
: readonly "${NORMAL_OUTPUT:=/dev/null}"
: readonly "${WORKING_DIRECTORY:=$PWD}"
: readonly "${ACK:=ack-grep}"

variable-names-length-per-php-project() {

    calculate-average() {
        local -a aScoreList
        local -i iAverage=0 iCount iIndex iTotal=0 iTotalCount=0
        local sScoreList

        readonly sScoreList="${1?One parameter required: <score-list>}"
        readonly aScoreList=( ${sScoreList//,/ } )

        for iIndex in "${!aScoreList[@]}"; do
            iCount="${aScoreList[${iIndex}]}"
            if [[ "${iCount}" -ne 0 ]];then
                iTotal=$(( iTotal + $((iCount * $((iIndex+1))))))
            fi
            iTotalCount=$(( iTotalCount + iCount ))
        done

        if [[ ${iTotalCount} -gt 0 ]];then
            iAverage=$(( iTotal / iTotalCount ));
        fi

        echo "${iAverage}"
    }

    calculate-minimum() {
        local -a aScoreList
        local -i iIndex iMinimum=0
        local sScoreList

        readonly sScoreList="${1?One parameter required: <score-list>}"
        readonly aScoreList=( ${sScoreList//,/ } )

        for iIndex in "${!aScoreList[@]}"; do
            if [[ "${aScoreList[${iIndex}]}" -ne 0 ]];then
                readonly iMinimum=$((iIndex + 1))
                break
            fi
        done

        echo "${iMinimum}";
    }

    calculate-maximum() {
        local -a aScoreList
        local -i iIndex iMaximum=0
        local sScoreList

        readonly sScoreList="${1?One parameter required: <score-list>}"
        readonly aScoreList=( ${sScoreList//,/ } )

        for iIndex in "${!aScoreList[@]}"; do
            if [[ "${aScoreList[${iIndex}]}" -ne 0 ]];then
                iMaximum=$((iIndex + 1))
            fi
        done

        echo "${iMaximum}";
    }

    clear-line() {
        tput cuu 1 && tput el
    }

    create-variables-list() {
        local -i iCounter iResult
        local sDirectory sPattern sResult=''

        readonly sDirectory="${1?One parameter required: <directory-to-scan>}"

        for iCounter in {1..50};do
            sPattern='\$[a-zA-Z0-9_]{'${iCounter}'}\b';
            iResult="$(create-ack-list "${sPattern}" "${sDirectory}" | wc -l)"
            sResult+="${iResult},"
        done

        # Remove trailing comma
        echo "${sResult:0:-1}"
    }

    create-ack-list() {
        local sDirectory sPattern

        readonly sPattern="${1?Two parameters required: <ack-pattern> <directory-to-scan>}"
        readonly sDirectory="${2?Two parameters required: <ack-pattern> <directory-to-scan>}"

        { "${ACK}" \
            --php \
            --ignore-file='match:/^.*(\.phar|Test.php)$/' \
            --match "${sPattern}" \
            -o \
            --ignore-dir=vendor --ignore-dir=var \
            "${sDirectory}" \
            | cut -d ':' -f 3 \
            | sort -u
        } || echo -n ''
    }

    fetch-github-list() {
        curl -s 'https://api.github.com/search/repositories?q=language%3APHP&sort=stars&language=php' \
            | grep 'clone_url' \
            | cut -d'"' -f4
    }

    print-topic() {
        echo -e " =====> $*\n" >> "${NORMAL_OUTPUT}"
    }

    print-status() {
        echo -e " -----> $*\n" >> "${NORMAL_OUTPUT}"
    }

    main() {
        local -a aProjects
        local sDetailResult sDirectory sFile sImagesUrl sProject sResult sSummaryResult sUrl

        mkdir -p "${WORKING_DIRECTORY}/build/"

        if [[ ! -f "${WORKING_DIRECTORY}/github.log" ]];then
            print-topic 'Fetching project list from Github'
            echo "$(fetch-github-list)" > "${WORKING_DIRECTORY}/github.log"
        fi

        readonly aProjects=( $(cat "${WORKING_DIRECTORY}/github.log" ) )

        for sUrl in "${aProjects[@]}";do
            print-topic 'Processing project list'

            sProject="$(echo "${sUrl:19:-4}" | tr '/' '_')"
            sDirectory="${WORKING_DIRECTORY}/${sProject}"

            if [[ "${sProject}" = '' ]];then
                echo " ! ERROR"
                exit 90
            fi

            if [[ ! -f "${WORKING_DIRECTORY}/build/${sProject}.txt" ]];then

                if [[ ! -d "${WORKING_DIRECTORY}/${sProject}" ]];then
                    print-status "Cloning ${sUrl}"
                    git clone ${sUrl} "${WORKING_DIRECTORY}/${sProject}"
                fi

                print-status "Creating list of variables for ${sProject}"
                echo "$(create-variables-list "${sDirectory}")" > "${WORKING_DIRECTORY}/build/${sProject}.txt"

                if [[ -d "${WORKING_DIRECTORY}/${sProject}" ]];then
                    rm -rdf "${WORKING_DIRECTORY}/${sProject}"
                fi
            fi
        done

        sSummaryResult=''
        sDetailResult=''

        for sFile in "${WORKING_DIRECTORY}/build/"*".txt";do
            sProject="$(basename ${sFile:0:-4} | tr '_' '/')"
            sResult="$(cat ${sFile})"

            iAverage=$(calculate-average "${sResult}")
            iMin=$(calculate-minimum "${sResult}")
            iMax=$(calculate-maximum "${sResult}")

            sImagesUrl="$(printf "${GRAPH_URL}" "${sProject}" "${sResult}")"

            # sSummaryResult+="$(printf '%2i (min:%2i | max:%2i) %s' "${iAverage}" "${iMin}" "${iMax}" "${sProject}")\n"
            sSummaryResult+="$(printf '| %2i | %2i | %2i | [%s](https://github.com/%s) |' "${iAverage}" "${iMin}" "${iMax}" "${sProject}" "${sProject}")\n"

            # if [[ ! -f "${WORKING_DIRECTORY}/build/${sProject}.png" ]];then
            #     print-status "Downloading graph image for ${sProject}"
            #     wget --quiet --output-document="${WORKING_DIRECTORY}/${sProject}.png" "${sImagesUrl}"
            # fi

            sDetailResult+="$(
                printf '\n\n### %s\n\n![](%s)\n\n- Average:  %2i\n- Shortest: %2i\n- Longest:  %2i\n' \
                    "${sProject}" "${sImagesUrl}" "${iAverage}" "${iMin}" "${iMax}"
            )"
        done

        # echo -e "${sSummaryResult}" | sort -bdf -k1,1nr -k5,5

        echo '| Average | Shortest | Longest | Project |'
        echo '| --- | --- | --- | ---|'
        echo -e "${sSummaryResult}" | sort -t'|' -bdf -k2,2nr -k6,6nr
        echo -e "${sDetailResult}"
    }

    main "${@}"
}

if [[ ${BASH_SOURCE[0]} != $0 ]]; then
    export -f variable-names-length-per-php-project
else
    variable-names-length-per-php-project "${@}"
    exit ${?}
fi

# EOF

AVNLIPPP

Average variable name length in popular PHP projects

Introduction

This project show the average length of variable names of the Top 30 (by ammount of stars) PHP projects on Github.

The logic that is used to retrieve these values has been included as a separate shell script.

Result

AverageShortestLongestProject
12148matomo-org/matomo
11149WordPress/WordPress
10127octobercms/october
10129composer/composer
10135phacility/phabricator
10139yiisoft/yii2
10141symfony/symfony
10147sebastianbergmann/phpunit
9129CachetHQ/Cachet
9132PHPOffice/PHPExcel
9136fzaninotto/Faker
9138getgrav/grav
8123bcit-ci/CodeIgniter
8123slimphp/Slim
8126Seldaek/monolog
8126dingo/api
8134cakephp/cakephp
8136laravel/framework
8319serbanghita/Mobile-Detect
7115domnikl/DesignPatternsPHP
7123barryvdh/laravel-debugbar
7123guzzle/guzzle
7123phalcon/cphalcon
7126phanan/koel
7128PHPMailer/PHPMailer
6117briannesbitt/Carbon
6117roots/sage
6216laravel/laravel
5120danielmiessler/SecLists
000flarum/flarum

CachetHQ/Cachet

  • Average: 9
  • Shortest: 1
  • Longest: 29

PHPMailer/PHPMailer

  • Average: 7
  • Shortest: 1
  • Longest: 28

PHPOffice/PHPExcel

  • Average: 9
  • Shortest: 1
  • Longest: 32

Seldaek/monolog

  • Average: 8
  • Shortest: 1
  • Longest: 26

WordPress/WordPress

  • Average: 11
  • Shortest: 1
  • Longest: 49

barryvdh/laravel-debugbar

  • Average: 7
  • Shortest: 1
  • Longest: 23

bcit-ci/CodeIgniter

  • Average: 8
  • Shortest: 1
  • Longest: 23

briannesbitt/Carbon

  • Average: 6
  • Shortest: 1
  • Longest: 17

cakephp/cakephp

  • Average: 8
  • Shortest: 1
  • Longest: 34

composer/composer

  • Average: 10
  • Shortest: 1
  • Longest: 29

danielmiessler/SecLists

  • Average: 5
  • Shortest: 1
  • Longest: 20

dingo/api

  • Average: 8
  • Shortest: 1
  • Longest: 26

domnikl/DesignPatternsPHP

  • Average: 7
  • Shortest: 1
  • Longest: 15

flarum/flarum

  • Average: 0
  • Shortest: 0
  • Longest: 0

fzaninotto/Faker

  • Average: 9
  • Shortest: 1
  • Longest: 36

getgrav/grav

  • Average: 9
  • Shortest: 1
  • Longest: 38

guzzle/guzzle

  • Average: 7
  • Shortest: 1
  • Longest: 23

laravel/framework

  • Average: 8
  • Shortest: 1
  • Longest: 36

laravel/laravel

  • Average: 6
  • Shortest: 2
  • Longest: 16

matomo-org/matomo

  • Average: 12
  • Shortest: 1
  • Longest: 48

octobercms/october

  • Average: 10
  • Shortest: 1
  • Longest: 27

phacility/phabricator

  • Average: 10
  • Shortest: 1
  • Longest: 35

phalcon/cphalcon

  • Average: 7
  • Shortest: 1
  • Longest: 23

phanan/koel

  • Average: 7
  • Shortest: 1
  • Longest: 26

roots/sage

  • Average: 6
  • Shortest: 1
  • Longest: 17

sebastianbergmann/phpunit

  • Average: 10
  • Shortest: 1
  • Longest: 47

serbanghita/Mobile-Detect

  • Average: 8
  • Shortest: 3
  • Longest: 19

slimphp/Slim

  • Average: 8
  • Shortest: 1
  • Longest: 23

symfony/symfony

  • Average: 10
  • Shortest: 1
  • Longest: 41

yiisoft/yii2

  • Average: 10
  • Shortest: 1
  • Longest: 39