aboutsummaryrefslogtreecommitdiff
blob: ecc3a35ba35714f88befb5a8b00f8a6191679f0e (plain)
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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
#!/bin/sh
#
# A hook to disallow php syntax errors to be committed
# by running php -l (lint) on them. It requires php-cli
# to be installed.
#
# This is a pre-commit hook.
#
# To install this you can either copy or symlink it to
# $GIT_DIR/hooks, example:
#
# ln -s ../../git-tools/hooks/pre-commit \\
#   .git/hooks/pre-commit

if [ -z "$PHP_BIN" ]
then
	PHP_BIN=php
fi

if [ "$(echo -e test)" = test ]
then
	echo_e="echo -e"
else
	echo_e="echo"
fi

# necessary check for initial commit
if git rev-parse --verify HEAD >/dev/null 2>&1
then
	against=HEAD
else
	# Initial commit: diff against an empty tree object
	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
fi

error=0
errors=""

if ! which "$PHP_BIN" >/dev/null 2>&1
then
	echo "PHP Syntax check failed:"
	echo "PHP binary does not exist or is not in path: $PHP_BIN"
	exit 1
fi

# dash does not support $'\n':
# http://forum.soft32.com/linux2/Bug-409179-DASH-Settings-IFS-work-properly-ftopict70039.html
IFS='
'
# get a list of staged files
for line in $(git diff-index --cached --full-index $against)
do
	# split needed values
	sha=$(echo $line | cut -d' ' -f4)
	temp=$(echo $line | cut -d' ' -f5)
	status=$(echo $temp | cut -d'	' -f1)
	filename=$(echo $temp | cut -d'	' -f2)

	# file extension
	ext=$(echo $filename | sed 's/^.*\.//')

	# only check files with php extension
	if [ $ext != "php" ]
	then
		continue
	fi

	# do not check deleted files
	if [ $status = "D" ]
	then
		continue
	fi

	# check the staged file content for syntax errors
	# using php -l (lint)
	# note: if display_errors=stderr in php.ini,
	# parse errors are printed on stderr; otherwise
	# they are printed on stdout.
	# we filter everything other than parse errors
	# with a grep below, therefore it should be safe
	# to combine stdout and stderr in all circumstances
	result=$(git cat-file -p $sha | "$PHP_BIN" -l 2>&1)
	if [ $? -ne 0 ]
	then
		error=1
		# Swap back in correct filenames
		errors=$(echo "$errors"; echo "$result" |sed -e "s@in - on@in $filename on@g")
	fi
done
unset IFS

if [ $error -eq 1 ]
then
	echo "PHP Syntax check failed:"
	# php "display errors" (display_errors php.ini value)
	# and "log errors" (log_errors php.ini value).
	# these are independent settings - see main/main.c in php source.
	# the "log errors" setting produces output which
	# starts with "PHP Parse error:"; the "display errors"
	# setting produces output starting with "Parse error:".
	# if both are turned on php dumps the parse error twice.
	# therefore here we try to grep for one version and
	# if that yields no results grep for the other version.
	#
	# other fun php facts:
	#
	# 1. in cli, display_errors and log_errors have different
	#    destinations by default. display_errors prints to
	#    standard output and log_errors prints to standard error.
	#    whether these destinations make sense is left
	#    as an exercise for the reader.
	# 2. as mentioned above, with all output turned on
	#    php will print parse errors twice, one time on stdout
	#    and one time on stderr.
	# 2. it is possible to set both display_errors and log_errors
	#    to off. if this is done php will print the text
	#    "Errors parsing <file>" but will not say what
	#    the errors are. useful behavior, this.
	# 3. on my system display_errors defaults to on and
	#    log_errors defaults to off, therefore providing
	#    by default one copy of messages. your mileage may vary.
	# 4. by setting display_errors=stderr and log_errors=on,
	#    both sets of messages will be printed on stderr.
	# 5. php-cgi binary, given display_errors=stderr and
	#    log_errors=on, still prints both sets of messages
	#    on stderr, but formats one set as an html fragment.
	# 6. your entry here? ;)
	$echo_e "$errors" | grep "^Parse error:"
	if [ $? -ne 0 ]
	then
		# match failed
		$echo_e "$errors" | grep "^PHP Parse error:"
	fi
	exit 1
fi