SVN pre-commit hook which can syntax check all files

If you manage a project which uses Subversion you’re going to eventually want to check the syntax of files before they get committed to the repository. Checking files before they get committed to the repository solves at least two problems for me.

  1. It stops me from checking something in, noticing it doesn’t work and committing a fixed version only moments later.
  2. It prevents other users from accidentally checking in broken code

A quick search online will reveal that it’s easy to check the syntax of your PHP files before they are committed. All you need to use is a pre-commit hook. Unfortunately scripts I found would all stop on the first error which was not the behavior I wanted. I wanted to check all of the files I was committing and get a list of all errors immediately. So, I wrote my own pre-commit hook.

Checking All PHP Files With A pre-commit Hook

The script I came up with is in PHP. Save this in the hooks directory of your Subversion repository. Name it pre-commit and make it executable.

#!/usr/bin/php
<?php 

// Set these manually since Subversion doesn't set ENV
$PHP = '/usr/bin/php';
$SVNLOOK = '/usr/bin/svnlook';
$AWK = '/usr/bin/awk';
$GREP = '/bin/egrep';
$SED = '/bin/sed'; 

$REPOS = $argv[1];
$TXN = $argv[2]; 

// Find the changes...
$CHANGED=`$SVNLOOK changed -t "$TXN" "$REPOS" | $GREP "^[U|A]" | $AWK '{print $2}'`;
// ...as an array
$CHANGED = split("\n",trim(rtrim($CHANGED))); 

$errors = Array();

// Perform specific actions based on the file extension
foreach($CHANGED as $FILE){
 switch(pathinfo($FILE,PATHINFO_EXTENSION)){
 case 'php':
 case 'class':
    // Get just the error/no error message from php -l
    $cmd="$SVNLOOK cat -t '$TXN' '$REPOS' '$FILE' | $PHP -l | head -2 | tail -1";
    $msg=trim(rtrim(`$cmd`));
    if(preg_match('/No syntax errors detected/',$msg) != 1){
       $msg = preg_replace('/in - /','',$msg);
       $errors[] = "In $FILE: $msg";
    }
   break;
  case 'js':
    // You could do something else for JavaScript -- like JSLint, if you're brave
    break;
 }
} 

// Print all the errors in a nice list
if(count($errors) > 0){
 $warning ="
*************************************************************************
* Please correct the following errors before commiting these changes! *
*************************************************************************
";
 error_log($warning);
 for($i = 1;$i <= count($errors);$i++){
 error_log("$i. " . $errors[($i - 1)]);
 } 

 exit(-1);
} 

exit(0);

This script will check all of the files that changed based on their file extension. You could check .js files one way and .php another way. You can extend this script by simply adding more cases to the switch statement.

The script collects all errors and then prints a nice list when it’s done. The output looks like this:

svn commit -m "pre-commit hook test"
Sending        test_scripts/info.php
Sending        test_scripts/user_read.php
Transmitting file data ..svn: Commit failed (details follow):
svn: Commit blocked by pre-commit hook (exit code 255) with output:

*************************************************************************
*  Please correct the following errors before commiting these changes!  *
*************************************************************************

1. In /test_scripts/info.php: Parse error: syntax error, unexpected $end on line 4
2. In /test_scripts/user_read.php: Parse error: syntax error, unexpected T_ENCAPSED_AND_WHITESPACE, expecting T_STRING or T_VARIABLE or T_NUM_STRING on line 8

I’m not going to say that it’s the most elegant output in the world but it does the job and I haven’t checked in a bad PHP file since.

Posted in Programming, Something Interesting | Tagged , , , , , , , , | Leave a comment

Moving a single WordPress Category to a New Site

I recently moved the Fridley Farmer content from this site over to http://fridleyfarmer.com, and wanted to move all of my content over there too. There are plenty of instructions on moving wordpress sites available online, I did a full database dump, copied all the files, then deleted the non Fridley Farmer content from the new site. You can also use the Import/Export functionality to export just a single category, but I didn’t know if that would include my pictures and comments as well, so I just copied everything.

What wasn’t obvious however, was how to redirect users to my new content! I didn’t want real users visiting stuporglue.org for the Fridley Farmer content anymore, but I didn’t want to send EVERYONE (like you!) over to the new site. I also didn’t want Google thinking that my new site just had duplicate content.

I ended up making three changes to my wordpress install in order to make the transition as seamless as possible.

 

Header Redirects on Single Posts and The Cateogry

In theme’s header.php I added a short bit of PHP that would detect users trying to visit the Fridley Farmer category archive, or any post in the Fridley Farmer category. It performs a 301 Moved Permanently HTTP header redirect. Easy peasy.

<?php
if( is_single() && in_category('Fridley Farmer') ){
 header ('HTTP/1.1 301 Moved Permanently');
 $newsite = "http://fridleyfarmer.com" . $_SERVER['REQUEST_URI'];
 header ('Location: '. $newsite);
}else if( is_category('Fridley Farmer') ) {
 header ('HTTP/1.1 301 Moved Permanently');
 $newsite = "http://fridleyfarmer.com" . $_SERVER['REQUEST_URI'];
 header ('Location: '. $newsite);
}
?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html ...

Replace Category Content With a “We’ve Moved” Message

I edited the code in wp-includes/post-template.php to modify the post’s content as it’s returned.

I changed:

function the_content($more_link_text = null, $stripteaser = 0) {
   $content = get_the_content($more_link_text, $stripteaser);
   $content = apply_filters('the_content', $content);
   $content = str_replace(']]>', ']]&gt;', $content);
   echo $content;
}

Into:

function the_content($more_link_text = null, $stripteaser = 0) {
 if(in_category("Fridley Farmer")){
   $content = "<p>The Fridley Farmer has moved its wagon down the road to
   <a href='http://fridleyfarmer.com'>http://fridleyfarmer.com</a>. All of our
   belongings, posts and content have arrived safely, and we're just waiting for
   you!.</p>
   <p>Stuporglue.org will remain as a place for all my programming and other
   non-gardening related ramblings.</p>
   <p>So, update your links, tell all your friends and come check out the new site!</p>";
  } else {
   $content = get_the_content($more_link_text, $stripteaser);
   $content = apply_filters('the_content', $content);
   $content = str_replace(']]>', ']]&gt;', $content);
  }
  echo $content;
}

Replace Post Title Links With Link to New Site

In wp-includes/link-template.php I edited the very end of the function get_permalink. I made it so that links to Fridley Farmer content go to the new site.

return apply_filters('post_link', $permalink, $post, $leavename);
}

Into:

$finalurl = apply_filters('post_link', $permalink, $post, $leavename);

  require_once('category-template.php');
  if(in_category('Fridley Farmer',get_the_ID())){
    $finalurl = str_replace(home_url(),'http://fridleyfarmer.com',$finalurl);
  }

return $finalurl;
}

I hope that helps someone out. It wasn’t difficult, but I couldn’t find how to do something like this documented anywhere.

It will probably get overwritten the next time I upgrade WordPress, but hopefully by then everyone who needs to know to go to the new site will already have done so.

Posted in Something Interesting | Tagged , , | Leave a comment

Adding Pcntl Support to a Shared Host

If you are using a shared hosting solution you may not have pcntl support compiled into the PHP you are using. It’s actually not terribly difficult to add it yourself if your host allows it, and if you have build tools available to you on your host. Here’s how I did it when we needed pcntl support on our Media Temple grid server (gs).

The following steps are all done on the server over SSH. If you don’t have SSH access you probably need a new hosting service.

Pcntl tools are only really useful if you are running command line scripts, such as writing a daemon in PHP.

The exact steps will be different for different hosting services of course, but this is what worked for me

Many thanks to http://www.crimulus.com/ for the headstart on this.

The first step is to find your PHP version

php --version

Find and Download That Version’s Source Code

Check here: http://php.net/releases/index.php

wget http://museum.php.net/php5/php-5.2.14.tar.bz2 (download it)
tar xjf php-5.2.14.tar.bz2 (extract it)

Enter The Pcntl Source Directory

cd php-5.2.14/ext/pcntl

 

Prepare phpize to Work With Your Environment

(if phpize is already installed this step may not be needed)

wget stuporglue.org/downloads/phpize
chmod +x phpize

Find the Build Directory for Your Version of PHP

cd /
find . -name "build" 2>/dev/null
./usr/share/apache2/build
./usr/local/php-5.3.2/lib/php/build
./usr/local/php-5.2.14/lib/php/build <========
./usr/local/php-4.4.9/lib/php/build
cd -

Edit phpize so that phpdir and includedir Point at That Location

1 #!/bin/sh
2
3 # Variable declaration
4 prefix='/usr'
5 exec_prefix="`eval echo ${prefix}`"
6 phpdir="$prefix/local/php-5.2.14/lib/php/build"
7 includedir="$prefix/local/php-5.2.14/lib/php"
8 aclocaldir="$prefix/share/aclocal"
9 builddir="`pwd`"
10 SED="/bin/sed"
...

Run phpize

./phpize

Configure and Make

Find php-config

(it’s probably in the bin directory of the php-5.2.14 directory you found earlier)

cd /
find . -name php-config 2>/dev/null
./usr/local/php-5.3.2/bin/php-config
./usr/local/php-5.2.14/bin/php-config <=========
./usr/local/php-4.4.9/bin/php-config
cd -

Run Configure

./configure --with-php-config=/usr/local/php-5.2.14/bin/php-config

Run Make

make

Install and Use It

Create a happy place for it

mkdir ~/php-modules
cp modules/pcntl.so ~/php-modules

Create a php.ini File

Make a php.ini in your home directory file with at least the following lines.

extension_dir=/path/to/your/home/php-modules
extension=pcntl.so

Run your script!

php -c /path/to/your/home/php.ini /path/to/your/php/script.php

 

Enjoy!

Posted in Something Interesting | Leave a comment