programming

These entries are mostly about programming, with an occasional IT post thrown in.

Due to working with Drupal for most of this year, I’ve seen this construct so many times it’s killed me a little inside:

switch($a) {
    case TRUE:
        switch($b) {
            case TRUE:
                break;
            case FALSE:
                break;
        }
        break;
    case FALSE:
        switch($b) {
            case TRUE:
                break;
            case FALSE:
                break;
        }
        break;
}

Someone I work with, who wishes to keep his name off the net, came up with this:

switch(array($a, $b)) {
    case array(false, false):
        break;
    case array(false, true):
        break;
    case array(true, false):
        break;
    case array(true, true):
        break;
}

I’d rather avoid the need for nested switches altogether, but this construct makes it easy to collapse similar branches, and is, I think, a useful addition to my toolkit:

switch(array($a,$b)) {
    case array(true, true):
        break;
    case array(false, false):
    case array(false, true):
    case array(true, false):
    default:
        break;
}

It’s worth mentioning that arrays are compared for equality (==) rather than identicality (===).

A fragment of PHP that really shouldn’t work. I hope nobody ever finds a use for this:

switch($n)
{
    case 1;
        echo 'one';
        break;
    case 2;
        echo 'two';
        break;
    case 3;
        echo 'three';
        break;
}

Check this out:

if (isset($a, $b, $c))
{
    unset($a, $b, $c);
}

Mulitple arguments. Looks like they’ve been there since an early PHP4 release, at least. Why does nobody tell me these things?

The problem:

If you include() or require() files with relative paths, APC will search the include_path on every request, and you won’t gain the full benefit of turning apc.stat off.

Using absolute paths portably in your own code is easy – just use a constant, eg. “require APP_PATH . ‘file.php’;”. External libraries, however, are harder to deal with – you either accept the performance hit, or go through them adding constants by hand (and merging your changes every time there’s a new release).

The solution:

This script rewrites PHP scripts, turning relative paths into absolute paths, allowing you to deploy third party libraries within your build process and still take full advantage of APC.

Some caveats:

It reads from stdin and writes to stdout.

It relies on it’s own include_path; in the real world, you’ll probably want to pass that in on the command line.

It only deals with the most common subset of include statements (variations on T_INCLUDE, T_CONSTANT_ENCAPSED_STRING, T_SEMICOLON).

The Windows support is a bit wing-and-a-prayer.

It’s memory-greedy when dealing with very large files.

It doesn’t play 100% nicely with safe_mode_include_dir.

Thrown out there in the hope it will be useful to someone. I’d be delighted to accept any feedback or additions anyone wants to offer.

Processing Blobs

As Processing recently hit 1.0 I thought I’d take a quick look. It seems to work on the same principle as a unit test suite or a web framework – you develop “inside” the library, so it can abstract all that Java complexity away from you. Fill out setup() and draw() methods, press a button to run, press a button to export as an applet. Embedding the applet in Wordpress is actually a lot more frustrating than writing the damn thing (Solution: I resorted to an iframe. I don’t really want to waste time because Wordpress is being “smart”).

Processing is an awful lot of fun. It evokes a feeling of exploration that I haven’t had since the BBC Basic days – experimenting with maths, mapping it to a plane, just to see what it looks like.

I doubt anyone reading this via RSS will be able to see the applet, but you’re only missing lens-flare-esque coloured blobs. Click and hold to draw. Right-click to clear.

Source code: TransparentEllipses

I’ve added Chris Dean to the list of web developers in Hampshire. I also updated the OPML summary.

I was trying to figure out the best way to test the myfputcsv() function I posted yesterday. Reading the data from disc before comparing it seemed like a step where errors could creep in, but there was no obvious way round that, as the purpose of the function is to write to disc.

Then I realised I could use PHP’s (fairly) new IO streams to dump the function’s output to a temporary buffer, and read it back in for comparison. Not perfect, but it removes concerns about file mutexes, permissions, unique filenames, etc. and speeds up the tests, as they never touch disc.

It wasn’t worth building on top of PHPUnit for this… checking failure conditions can wait for another day. I just wrote a quick script that compares the output of fputcsv() and myfputcsv(). (And it worked – I’ve already fixed two errors in yesterday’s post).

This is the first time I’ve reached for php://memory. It’s obviously useful for testing code that writes to disc, but I’m now wondering where else it might come in handy.

<?php
 
require_once 'myfputcsv.php';
 
function write( $function, $array )
{
    $fp = fopen( 'php://memory', 'w+' );
    $function( $fp, $array );
    rewind( $fp );
    return fread( $fp, 1024 );
}
 
function test( $array )
{
    $fputcsv = write( 'fputcsv', $array );
    $myfputcsv = write( 'myfputcsv', $array );
    if ( assert( '$fputcsv === $myfputcsv' ) )
    {
        echo "OK" . PHP_EOL;
    }
}
 
test( array() );
test( array( "Hello", "World" ) );
test( array( "He\nllo", "World" ) );
test( array( "He\\"llo", "World" ) );
test( array( "He llo", "World" ) );
test( array( "He\tllo", "World" ) );
test( array( "He\"\"llo", "World" ) );
test( array( "He,llo", "World" ) );

I recently needed an fputcsv() with a couple of modifications (I needed fields quoted unconditionally, and more than one character in the delimiter field). I looked at a couple of versions from the man page comments, but they were buggy in one way or another, and PHP4-specific.

The function below is as close as I can get to fputcsv()’s behaviour. I’m throwing it out there in the hope that it will be useful to someone, someday. It should be easy enough to modify to suit specific requirements.

/**
 * myfputcsv()
 *
 * Mimics the observed behaviour of PHP's fputcsv()
 *
 * Requires at least PHP 5.2.0 due to reliance on __toString
 *
 * @param resource $fp valid file pointer
 * @param array $fields array of values
 * @param string $delimiter optional parameter sets the field delimiter character. Defaults to ','
 * @param string $enclosure optional parameter sets the field enclosure character. Defaults to '"'
 * @return bool the length of the written string, or FALSE on failure
 */
function myfputcsv( $fp, $fields, $delimiter = ',', $enclosure = '"' )
{
    /**
     * Validate incoming values
     *
     * Weird corner cases are checked for here, so we can mimic fputcsv() as closely
     * as possible. Eg we check whether or not an object passed as $delimiter or
     * $enclosure implements __toString
     */
    if ( !is_resource( $fp ) )
    {
        trigger_error( __FUNCTION__ . '() expects parameter 1 to be resource, ' . gettype( $fp ) . ' given', E_USER_WARNING );
        return false;
    }
    if ( !is_array( $fields ) )
    {
        trigger_error( __FUNCTION__ . '() expects parameter 2 to be array, ' . gettype( $fields ) . ' given', E_USER_WARNING );
        return false;
    }
    if ( is_object( $delimiter ) && method_exists( $delimiter, '__toString' ) )
    {
        $delimiter = ( string ) $delimiter;
    }
    if ( is_object( $enclosure ) && method_exists( $enclosure, '__toString' ) )
    {
        $enclosure = ( string ) $enclosure;
    }
    if ( $delimiter == null )
    {
        trigger_error( __FUNCTION__ . '(): delimiter must be a character', E_USER_WARNING );
        return false;
    }
    if ( $enclosure == null )
    {
        trigger_error( __FUNCTION__ . '(): enclosure must be a character', E_USER_WARNING );
        return false;
    }
    if ( !is_scalar( $delimiter ) )
    {
        trigger_error( __FUNCTION__ . '() expects parameter 3 to be string, ' . gettype( $delimiter ) . ' given', E_USER_WARNING );
        return false;
    }
    if ( !is_scalar( $enclosure ) )
    {
        trigger_error( __FUNCTION__ . '() expects parameter 4 to be string, ' . gettype( $enclosure ) . ' given', E_USER_WARNING );
        return false;
    }
    if ( strlen( $delimiter ) > 1 )
    {
        trigger_error( __FUNCTION__ . '(): delimiter must be a single character', E_USER_NOTICE );
        $delimiter = $delimiter[0];
    }
    if ( strlen( $enclosure ) > 1 )
    {
        trigger_error( __FUNCTION__ . '(): enclosure must be a single character', E_USER_NOTICE );
        $enclosure = $enclosure[0];
    }
 
    /**
     * Prepare fields for writing to file by escaping them and wrapping them
     * in $enclosure
     */
    for( $i = 0; $i < sizeof( $fields ); $i++ )
    {
        /**
         * Make a decision on whether or not to use $enclosure
         */
        $use_enclosure = false;
        if ( strpos( $fields[$i], $delimiter ) !== false )
        {
            $use_enclosure = true;
        }
        if ( strpos( $fields[$i], $enclosure ) !== false )
        {
            $use_enclosure = true;
        }
        if ( strpos( $fields[$i], "\" ) !== false )
        {
            $use_enclosure = true;
        }
        if ( strpos( $fields[$i], "\n" ) !== false )
        {
            $use_enclosure = true;
        }
        if ( strpos( $fields[$i], "\r" ) !== false )
        {
            $use_enclosure = true;
        }
        if ( strpos( $fields[$i], "\t" ) !== false )
        {
            $use_enclosure = true;
        }
        if ( strpos( $fields[$i], " " ) !== false )
        {
            $use_enclosure = true;
        }
 
        if ( $use_enclosure == true )
        {
            $fields[$i] = explode( "\$enclosure", $fields[$i] );
            for( $j = 0; $j < sizeof( $fields[$i] ); $j++ )
            {
                $fields[$i][$j] = explode( $enclosure, $fields[$i][$j] );
                $fields[$i][$j] = implode( "{$enclosure}{$enclosure}", $fields[$i][$j] );
            }
            $fields[$i] = implode( "\$enclosure", $fields[$i] );
            $fields[$i] = "{$enclosure}{$fields[$i]}{$enclosure}";
        }
    }
 
    /**
     * Write fields as a $delimiter-delimited string, and return number of
     * bytes written
     */
    return fwrite( $fp, implode( $delimiter, $fields ) . "\n" );
}

A base class is "a class from which other classes are derived".

Many OO languages have the concept of a single base class from which all other classes are explicitly or implicitly descended. For example, Ruby, Java and .NET all have Object.

It’s a very common belief that PHP implements stdClass as a base class for all objects, but this is in fact not the case:

<?php
 
class DoesNotExtend {}
 
class DoesExtend extends stdClass {}
 
$doesNotExtend = new DoesNotExtend();
$doesExtend = new DoesExtend();
 
var_dump($doesNotExtend instanceof stdClass);
var_dump($doesExtend instanceof stdClass);

Outputs:

bool(false)
bool(true)

When I started in web development a few years ago I had to move to Birmingham and London to find work – the local web industry was pretty much DOA. I’m not sure why that was… possibly companies gravitated towards the fibre in Docklands and the financing in the city, or London’s pool of experienced workers was larger. Anyway, technology on the South Coast tended to be focused on defence contracting.

Today there are certainly more developers out there, but they’re not very visible – a Google search for “PHP Developer Portsmouth” throws up pages of job ads, rather than pages of local PHP developers. Other technology- and location-specific searches return similar results.

In an effort to change this, and to make the local development community a bit more “joined-up”, I’ve been collecting local developers’ blogs. I’m hopeful that in the long-term these blogs will form the core of a local web development community, but at the very least, if we talk to each other, if we’re active online and network around subjects of common interest, it will raise everyone’s profile. I know that Adam Wintle wants to do similar things over at Refresh Portsmouth, so anyone who’s keen on real-world meet-ups should talk to him.

If you’re at all interested in web development on the South Coast, please import this handy OPML file into your RSS reader, and check back for updates occasionally. Alternatively, the HTML list is after the break. If you want to be added to the list, or you know someone who should be on there, please just let me know.
Read the rest of this entry »

« Older entries