Monday, 10 March 2008

Using PHP_CodeSniffer in an SVN pre-commit hook

I've just commit a new script to PHP_CodeSniffer called phpcs-svn-pre-commit. It sits in the scripts dir with phpcs and phpcs.bat. This script was contributed by Jake Bates, who has also volunteered to maintain the Debian package, and will be available in the 1.1.0 release.

Using the script is pretty easy, but you'll need to modify it slightly.

Edit /path/to/PHP_CodeSniffer/scripts/phpcs-svn-pre-commit and replace @php_bin@ in the first line with the path to the PHP CLI. For example,

#!@php_bin@
becomes
#!/usr/bin/php

Then, ensure the path to svnlook is correct by modifying the line:
define('PHP_CODESNIFFER_SVNLOOK', '/usr/bin/svnlook');

Now, add the following line to your pre-commit file in the SVN hooks directory:
/.../phpcs-svn-pre-commit "$REPOS" -t "$TXN" >&2 || exit 1

This will cause the SVN commit to fail if PHP_CodeSniffer finds any errors. The error report will be displayed to the user so they can fix errors before attempting the commit again.

You can also use all the standard phpcs command line options to do things like set the standard to use, the tab width and the error report format:
/.../phpcs-svn-pre-commit --standard=Squiz --tab-width=4 ...

And some example output:
$ svn commit -m "Test" temp.php
Sending temp.php
Transmitting file data .svn: Commit failed (details follow):
svn: 'pre-commit' hook failed with error output:

FILE: temp.php
---------------------------------------------------------------
FOUND 1 ERROR(S) AND 0 WARNING(S) AFFECTING 1 LINE(S)
---------------------------------------------------------------
2 | ERROR | Missing file doc comment
---------------------------------------------------------------

17 comments:

Anonymous said...

Can the script be configured to be not so nazi? E.g. allowing 1 CS error / 10 lines or that the overall CS error count may not increase (if you only change some lines of a file)

Greg Sherwood said...

Not with this script. You'd have to copy the phpcs-svn-pre-commit script and modify how it exits. You are able to get the total error and warning count, and the number of lines, so you could add whatever calculations you want.

Philippe Jausions said...

Hi Greg,

A good start for a helpful pre-commit hook. However there are situations where an error is almost certain, and nothing can be done about it. For instance, I just submitted a PEAR proposal for a stream package, and per the stream API, the class cannot satisfy the PEAR coding standards due to method names like stream_open(), stream_close(), etc... So a pre-commit hook would never let such valid code go through. A whitelist might be useful, but that requires PHP_CodeSniffer to recognize and fingerprint the error.

Anyway, PHP_CS is great! Keep up the good work.

gerard said...

Started writing my own pre-commit hook for CodeSniffer when I ran across this. Yours is, shall I say, a bit more robust?

Any hint on when it might be released?

Greg Sherwood said...

@Philippe
You are right, and I wont be stopping commits in work projects using this hook, although I'll probably write a post-commit hook to name and shame people :)

@Gerard
I don't have a fixed date for the next release, but it should be in the next couple of weeks. You can checkout PHP_CodeSniffer from cvs.php.net and install it using PEAR (or run it stand-alone) if you want it sooner. Info is here: http://pear.php.net/manual/en/installation.cvs.php

Marcello Duarte said...

@Greg
Let this good ideas keep coming

@Philippe
The value you have on your flag standard defines which standard class will be used for validating your commit. If you want something like a specialization of PEAR or Zend, you can extend that Standard Class using a white list as a parameter of your sub-class.

Anonymous said...

I installed PHP_CodeSniffer using pear and copied the scripts folder to path-where-phpcodesniffer-exists
The pre-commit hook looks something like this

#!/bin/sh

REPOS="$1"
TXN="$2"

/usr/local/php5/lib/php/PHP/CodeSniffer/scripts/phpcs-svn-pre-commit --standard=Himanshu "$REPOS" -t "$TXN" >&2 || exit 1


the phpcs-svn-pre-commit has the php path as

/usr/local/php5/bin/php.

On committing a php code it doesn't show any error whereas if I use

phpcs --standard=Himanshu test.php

I get the error.

FILE: /usr/local/svn/Test/hooks/test.php
--------------------------------------------------------------------------------
FOUND 1 ERROR(S) AND 0 WARNING(S) AFFECTING 1 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Spaces must be used to indent lines; tabs are not allowed
--------------------------------------------------------------------------------


What could be the reason ?

Greg Sherwood said...

@Anonymous Try using the latest code from CVS. There is a bug in the latest version: http://pear.php.net/bugs/bug.php?id=16054

Anonymous said...

Hey Greg,

Thanks for replying. Well could you send me the correct path from where I can download this.

Moreover do I need to compile PHP_CodeSniffer (if not using pear install)

Anonymous said...

Hey Greg,

Works fine with PHP_CodeSniffer-1.2.0RC1.

Thanks a ton !!

Cheers !!

Anonymous said...

Hey Greg,

My repository setup is something like this.

---- Development
Repo---- QA
---- Production

Now with pre-commit hook in place if I try to add any directory in any of the trunks it returns an error.

[root@localhost Production]# svn add Release0/
A Release0
[root@localhost Production]# svn commit -m "done"
Adding Production/Release0
svn: Commit failed (details follow):
svn: Commit blocked by pre-commit hook (exit code 1) with output:
svnlook: Path 'Production/Release0' is not a file

Is there a way I could tell the script to check only php scripts and exclude everything else. Also when I tried to commit a file x.php

I get the following error
--------------------------------------------------------------------------------
FOUND 1 ERROR(S) AND 0 WARNING(S) AFFECTING 1 LINE(S)
--------------------------------------------------------------------------------
2 | ERROR | Spaces must be used to indent lines; tabs are not allowed
--------------------------------------------------------------------------------

On removing the extra spaces I am able to commit this file but later when I delete this file I get the following error

[root@localhost Resdex]# svn commit -m "done"
Deleting x.php
svn: Commit failed (details follow):
svn: Commit blocked by pre-commit hook (exit code 1) with output:
svnlook: Try 'svnlook help' for more info
svnlook: Missing repository path argument


Please help !!

Himanshu said...

Hi,

I am having difficulty using CodeSniffer with SVN. The phpcs-svn-pre-commit script works fine with files but not with directory i.e. everytime I try to add a directory the pre-commit hook gives the following error

Transmitting file data .svn: Commit failed (details follow):
svn: Commit blocked by pre-commit hook (exit code 1) with output:
svnlook: Path 'b' is not a file
PHP Fatal error: Uncaught PHP_CodeSniffer_Exception: Could not auto-detect line endings from content in /usr/local/php5/lib/php/PHP

What could be the reason for the same? I installed PHP_CodeSniffer using pear and phpcs version is 1.2.0RC1.

Also, on deleting a file that was earlier committed (after making changes in the php file) it gives error

svn: Commit failed (details follow):
svn: Commit blocked by pre-commit hook (exit code 1) with output:
svnlook: Try 'svnlook help' for more info
svnlook: Missing repository path argument
PHP Fatal error: Uncaught PHP_CodeSniffer_Exception: Could not auto-detect line endings from content in /usr/local/php5/lib/php/PHP/CodeSniffer/File.php on line 435


Thanks in advance.

Greg Sherwood said...

@Himanshu and Anonymous: I'll take a look. Probably just a bug. I encourage you to submit bug reports to the official bug tracker by clicking the link at: http://pear.php.net/package/PHP_CodeSniffer

Jeroen said...

Hi,

I'm trying to configure the pre-commit-hook. The script gets started, however there are never errors reported.

If I run the CLI script phpcs over the file I'm committing, I've 22 errors.

Any idea what could be wrong? I've tried both the latest stable as the latest beta version.

Any advice would be very appreciated!

solomongaby said...

i have managed to add the precommit script but i get no errors.

I have another check for a log message after the codeSniffer check ... and i get prepended this string:
[M m8
i think that is the output from code sniffer.

can you please help me

Anonymous said...

Hey Greg,

I have a tree structure within the repo i.e a repo has following branches

1> Dev
2> QA
3> Prod

How can I restrict php code sniffer only to Dev and QA branches and not production??

Thanks in advance !!

Greg Sherwood said...

@Anonymous I'm not 100% sure. I think you do it in the commit hook script itself and not touch anything in PHP_CodeSniffer or the command that runs it, but I'm not very familiar enough with those hooks to give an example.