├── .gitignore ├── .github └── workflows │ └── shellcheck.yaml ├── README.md ├── LICENSE ├── config.dist └── pre-commit /.gitignore: -------------------------------------------------------------------------------- 1 | # Create your own config file ;) 2 | config 3 | -------------------------------------------------------------------------------- /.github/workflows/shellcheck.yaml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | 6 | name: "Shellcheck" 7 | permissions: {} 8 | 9 | jobs: 10 | shellcheck: 11 | name: Shellcheck 12 | runs-on: ubuntu-latest 13 | steps: 14 | - uses: actions/checkout@v4 15 | - name: Run ShellCheck 16 | uses: ludeeus/action-shellcheck@master 17 | with: 18 | severity: warning 19 | format: tty 20 | scandir: '.' 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # php-pre-commit 2 | 3 | Git pre-commit hook file for PHP projects 4 | 5 | ![Shellcheck status](https://github.com/devdrops/php-pre-commit/actions/workflows/shellcheck.yaml/badge.svg?branch=main) 6 | 7 | ## How to Use? 8 | 9 | 1. `git clone` this repository. 10 | 2. Copy the `pre-commit` file to your project's Git hooks folder. Make sure that the `pre-commit` file is executable. 11 | 2. Now copy the `config.dist` file to your Git hooks folder, and rename it to `config`. 12 | 3. Edit the `config` file with your preferences. 13 | 14 | Kudos for [Johnathan Pulos](https://github.com/codemis) for [his amazing Gist](https://gist.github.com/codemis/8225337)! 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Davi Marcondes Moreira 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /config.dist: -------------------------------------------------------------------------------- 1 | # Enable tools 2 | PHPCS_ACTIVE=1 3 | PHPMD_ACTIVE=1 4 | 5 | # Path to binaries. 6 | PHPCS_BIN=/usr/local/bin/phpcs 7 | PHPMD_BIN=/usr/local/bin/phpmd 8 | 9 | # The PHP CodeSniffer coding standard, you can also specify a path to your 10 | # own standard here. 11 | # e. g. /path/to/my/standard/dir/ 12 | PHPCS_CODING_STANDARD=PSR2 13 | 14 | # Comma-separated list of file patterns being ignored by PHP CodeSniffer. 15 | PHPCS_IGNORE= 16 | 17 | # Comma-seperated list of sniffs from PHP CodeSniffer that should be used. 18 | # Use `phpcs --standard=PSR1 -e` to list sniffs for your standard. 19 | PHPCS_SNIFFS=Generic.Files.ByteOrderMark,Generic.PHP.DisallowShortOpenTag 20 | 21 | # Egrep compatible pattern of files to be checked by PHP CodeSniffer. 22 | PHPCS_FILE_PATTERN="\.(php)$" 23 | 24 | # Ignore PHP CodeSniffer warnings. 25 | PHPCS_IGNORE_WARNINGS=1 26 | 27 | # PHP CodeSniffer Encoding. 28 | PHPCS_ENCODING=utf-8 29 | 30 | # PHP Mess Detector output mode (default: text). 31 | PHPMD_OUTPUT_MODE=text 32 | 33 | # PHP Mess Detector patterns (defaults: cleancode). 34 | PHPMD_PATTERNS=cleancode,codesize,controversial,design,naming,unusedcode 35 | 36 | # PHP Mess Detector file suffixes 37 | PHPMD_SUFFIXES=php 38 | 39 | # PHP Mess Detector list of files/folders to exclude 40 | PHPMD_EXCLUDE= 41 | 42 | # List of file patterns to exclude (using `grep`) 43 | GIT_EXCLUDE= 44 | -------------------------------------------------------------------------------- /pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ########## 4 | # Git Pre-Commit file for PHP projects 5 | ### 6 | # 7 | # This hook performs the following validation: 8 | # - PHP CodeSniffer 9 | # - PHP Mess Detector 10 | # 11 | # Based on https://gist.github.com/codemis/8225337 12 | # 13 | # @author Davi Marcondes Moreira 14 | # 15 | ########## 16 | 17 | 18 | ########## 19 | # DEFAULT SETTINGS 20 | ### 21 | # 22 | # These variables define the basic values for Code_Sniffer and PHPMD. 23 | # Override these by creating a new variable on the `config` file. 24 | # 25 | ########## 26 | PHPCS_ACTIVE=1 27 | PHPCS_BIN=/usr/bin/phpcs 28 | PHPCS_CODING_STANDARD=PEAR 29 | PHPCS_IGNORE= 30 | PHPMD_ACTIVE=1 31 | PHPMD_BIN=/usr/bin/phpmd 32 | PHPMD_OUTPUT=text 33 | PHPMD_PATTERNS_LIST=cleancode,codesize,controversial,design,naming,unusedcode 34 | TMP_STAGING="/tmp/.tmp_staging" 35 | 36 | 37 | ########## 38 | # Parse config file. 39 | ########## 40 | CONFIG_FILE=$(dirname $0)/config 41 | if [ -e $CONFIG_FILE ]; then 42 | . $CONFIG_FILE 43 | fi 44 | 45 | 46 | ########## 47 | # First: check if PHP Code_Sniffer and PHPMD bin files are present && executable. 48 | ########## 49 | if [ ! -x $PHPCS_BIN ] || [ ! -x $PHPMD_BIN ]; then 50 | tput setaf 1; echo "Executable not found. Check $PHPCS_BIN and $PHPMD_BIN." 51 | exit 1 52 | fi 53 | 54 | 55 | ########## 56 | # Git Check-up 57 | ########## 58 | if git rev-parse --verify HEAD 59 | then 60 | against=HEAD 61 | else 62 | # Initial commit: diff against an empty tree object 63 | against=4b825dc642cb6eb9a060e54bf8d69288fbee4904 64 | fi 65 | 66 | # This is the magic: 67 | # Retrieve all files in staging area that are ADDED, MODIFIED or RENAMED, 68 | # but no deletions etc. 69 | # Lets first check if there are any file pattern to exclude from this list. 70 | if [ "$GIT_EXCLUDE" != "" ]; then 71 | GIT_EXCLUDE_LIST="| grep -v $GIT_EXCLUDE" 72 | else 73 | GIT_EXCLUDE_LIST="" 74 | fi 75 | 76 | 77 | FILES=$(git diff-index --name-only --cached --diff-filter=ACMR $against -- $GIT_EXCLUDE_LIST) 78 | 79 | if [ "$FILES" == "" ]; then 80 | exit 0 81 | fi 82 | 83 | 84 | # Create temporary copy of staging area 85 | if [ -e $TMP_STAGING ]; then 86 | rm -rf $TMP_STAGING 87 | fi 88 | mkdir $TMP_STAGING 89 | 90 | # Match files against whitelist 91 | FILES_TO_CHECK="" 92 | for FILE in $FILES 93 | do 94 | echo "$FILE" | egrep -q "$PHPCS_FILE_PATTERN" 95 | RETVAL=$? 96 | if [ "$RETVAL" -eq "0" ] 97 | then 98 | FILES_TO_CHECK="$FILES_TO_CHECK $FILE" 99 | fi 100 | done 101 | 102 | if [ "$FILES_TO_CHECK" == "" ]; then 103 | exit 0 104 | fi 105 | 106 | 107 | ########## 108 | # Validate PHP CodeSniffer variables 109 | ########## 110 | if [ "$PHPCS_ACTIVE" != "1" ]; then 111 | PHPCS_ACTIVE=0 112 | fi 113 | 114 | if [ "$PHPCS_IGNORE" != "" ]; then 115 | IGNORE="--ignore=$PHPCS_IGNORE" 116 | else 117 | IGNORE="" 118 | fi 119 | 120 | if [ "$PHPCS_CODING_STANDARD" != "" ]; then 121 | PHPCS_CODING_STANDARD="--standard=$PHPCS_CODING_STANDARD" 122 | else 123 | PHPCS_CODING_STANDARD="" 124 | fi 125 | 126 | if [ "$PHPCS_SNIFFS" != "" ]; then 127 | SNIFFS="--sniffs=$PHPCS_SNIFFS" 128 | else 129 | SNIFFS="" 130 | fi 131 | 132 | if [ "$PHPCS_ENCODING" != "" ]; then 133 | ENCODING="--encoding=$PHPCS_ENCODING" 134 | else 135 | ENCODING="" 136 | fi 137 | 138 | if [ "$PHPCS_IGNORE_WARNINGS" == "1" ]; then 139 | IGNORE_WARNINGS="-n" 140 | else 141 | IGNORE_WARNINGS="" 142 | fi 143 | 144 | 145 | ########## 146 | # Validate PHP Mess Detector variables 147 | ########## 148 | if [ "$PHPMD_ACTIVE" != "1" ]; then 149 | PHPMD_ACTIVE=0 150 | fi 151 | 152 | if [ "$PHPMD_OUTPUT_MODE" != "" ]; then 153 | PHPMD_OUTPUT="$PHPMD_OUTPUT_MODE" 154 | else 155 | PHPMD_OUTPUT="text" 156 | fi 157 | 158 | if [ "$PHPMD_PATTERNS" != "" ]; then 159 | PHPMD_PATTERNS_LIST="$PHPMD_PATTERNS" 160 | else 161 | PHPMD_PATTERNS_LIST="cleancode" 162 | fi 163 | 164 | if [ "$PHPMD_SUFFIXES" != "" ]; then 165 | PHPMD_SUFFIXES_LIST="--suffixes $PHPMD_SUFFIXES" 166 | else 167 | PHPMD_SUFFIXES_LIST="" 168 | fi 169 | 170 | if [ "$PHPMD_EXCLUDE" != "" ]; then 171 | PHPMD_EXCLUDE_LIST="--exclude $PHPMD_EXCLUDE" 172 | else 173 | PHPMD_EXCLUDE_LIST="" 174 | fi 175 | 176 | 177 | ########## 178 | # Copy contents of staged version of files to temporary staging area 179 | # because we only want the staged version that will be commited and not 180 | # the version in the working directory. 181 | ########## 182 | STAGED_FILES="" 183 | for FILE in $FILES_TO_CHECK 184 | do 185 | ID=$(git diff-index --cached $against $FILE | cut -d " " -f4) 186 | ########## 187 | # Create staged version of file in temporary staging area with the same 188 | # path as the original file so that the phpcs ignore filters can be applied. 189 | ########## 190 | mkdir -p "$TMP_STAGING/$(dirname $FILE)" 191 | git cat-file blob $ID > "$TMP_STAGING/$FILE" 192 | STAGED_FILES="$STAGED_FILES $TMP_STAGING/$FILE" 193 | done 194 | 195 | 196 | ########## 197 | # CODE INSPECTION: PHP CodeSniffer 198 | ########## 199 | if [ "$PHPCS_ACTIVE" == "1" ]; then 200 | echo "" 201 | tput setaf 12; echo " :: PHP CodeSniffer inspection :: " 202 | PHPCS_OUTPUT=$($PHPCS_BIN -s $IGNORE_WARNINGS $PHPCS_CODING_STANDARD $ENCODING $IGNORE $STAGED_FILES) 203 | PHPCS_RETVAL=$? 204 | 205 | if [ $PHPCS_RETVAL -ne 0 ]; then 206 | tput setaf 1; echo " ✘ Issues found: " 207 | tput setaf 7; echo "$PHPCS_OUTPUT" 208 | 209 | rm -rf $TMP_STAGING 210 | 211 | exit $PHPCS_RETVAL 212 | else 213 | tput setaf 2; echo " ✔ Inspection is OK!" 214 | fi 215 | else 216 | echo "" 217 | tput setaf 8; echo " ➔ PHP CodeSniffer inspection is OFF." 218 | fi 219 | 220 | 221 | ########## 222 | # CODE INSPECTION: PHP Mess Detector 223 | ########## 224 | if [ "$PHPMD_ACTIVE" == "1" ]; then 225 | echo "" 226 | tput setaf 12; echo " :: PHP Mess Detector inspection :: " 227 | PHPMD_OUTPUT=$($PHPMD_BIN $STAGED_FILES $PHPMD_OUTPUT $PHPMD_PATTERNS_LIST $PHPMD_SUFFIXES_LIST $PHPMD_EXCLUDE_LIST) 228 | PHPMD_RETVAL=$? 229 | 230 | if [ $PHPMD_RETVAL -ne 0 ]; then 231 | tput setaf 1; echo " ✘ Issues found: " 232 | tput setaf 7; echo "$PHPMD_OUTPUT" 233 | 234 | rm -rf $TMP_STAGING 235 | 236 | exit $PHPMD_RETVAL 237 | else 238 | tput setaf 2; echo " ✔ Inspection is OK!" 239 | fi 240 | else 241 | echo "" 242 | tput setaf 8; echo " ➔ PHP Mess Detector inspection is OFF." 243 | fi 244 | 245 | tput setaf 12; 246 | 247 | 248 | 249 | rm -rf $TMP_STAGING 250 | 251 | 252 | 253 | echo "" 254 | exit 0; --------------------------------------------------------------------------------