Category Archives: PHP

Using PHP's NumberFormatter to format currencies

I've been using number_format() for a very long time, but recently discovered that within the intl extension there's a NumberFormatter class available too. This is quite a clever class as it is Locale aware and handles formatting currency, including the correct symbol.

You can check if you have the intl extension installed using php -m | grep intl and if you don't then you can install it with apt-get install php5-intl or yum install php-intl assuming you use your distro's stock PHP. (If you compile your own, then --enable-intl is the switch you need.)

Consider this scenario where I want to display a price in Euros for both the UK and the German markets:

$amount = '12345.67';

$formatter = new NumberFormatter('en_GB',  NumberFormatter::CURRENCY);
echo 'UK: ' . $formatter->formatCurrency($amount, 'EUR') . PHP_EOL;

$formatter = new NumberFormatter('de_DE',  NumberFormatter::CURRENCY);
echo 'DE: ' . $formatter->formatCurrency($amount, 'EUR') . PHP_EOL;

The constructor takes the locale to use along with the type of formatting you want to do, CURRENCY in this case. Then, when you call formatCurrency(), the second parameter is the 3-letter alphabetic ISO 4217 code for the currency you want to display.

The output of the code above is:

UK: €12,345.68
DE: 12.345,68 €

You can see the locale at work here as in the UK we expect to see the currency symbol before the number and use comma to separate thousands, whereas in Germany, the currency symbol is (usually!) at the end of the number and the decimal point is used to separate the thousands.

The ISO 4217 currency code determines which currency symbol is returned. Obviously, for EUR, we get the € symbol. For GBP, we'd get £, etc.

Finally, if you use ZF2, this is all wrapped up into the currencyFormat view helper for you.

Objects in the model layer: Part 2

I previously talked about the terms I use for objects in the model layer and now it's time to put some code on those bones. Note that,as always, all code here is example code and not production-ready.

An entity

My entities are plain old PHP objects:

namespace Book;

class Entity
    protected $id;
    protected $author;
    protected $title;
    protected $isbn;

    public function __construct($data = array())

    // Data transfer methods

    public function populate($data)
        if (array_key_exists('id', $data)) {
        // repeat for other properties

    public function getArrayCopy()
        return array(
            'id'     => $this->getId(),
            // repeat for other properties

    // Entity-specific methods

    public function isValidIsbn()
        // validate ISBN and return true/false

    // Property getters and setters
    public function getId()
        return $this->id;
    public function setId($id)
        $this->id = $id;
        return $this;
    // Repeat for other properties...


There's nothing particularly complicated here. We have an object with a number of properties and some methods. This object represents a book, so we have properties of author, title and isbn. We need to be able to set and retrieve the properties so there is a get and set method for each one (only getId() and setId() are in the code snippet above to save space!)

Generally, I populate an entity from a mapper and use a pair of methods to do this: populate() and getArrayCopy(). These methods transfer the data in the properties to and from an array.

There are also entity-specific methods within the entity. For this object, I have a method called isValidIsbn(); for a user object, I may have a method called getFullName() which concatenates the user's first name and surname.

A mapper

The mapper knows how to load and save entities. This is a hand-rolled one:

namespace Book;

use PDO;
use BookEntity;

class Mapper
    protected $pdo;

    public function __construct($dsn, $username, $password)
        $this->pdo = new PDO($dsn, $username,  $password);

    public function loadById($id)
        $sql = 'SELECT * FROM book WHERE id = :id';

        $statement = $this->pdo->prepare($sql);
        $statement->execute(array('id' => $id));

        $result = $statement->fetch();
        if ($result) {
            $book = new Entity($result);
            return $book;

        return false;

    public function fetchAll($order)
        // Select all books from database using PDO
        // iterate over each one and create a BookEntity object

    public function save(Entity $book)
        $data = $book->getArrayCopy();
        if ($data['id'] > 0) {
            // Update data in table using PDO and set $result
        } else {
            // Insert data into table using PDO and set $result

        return $result;

    public function delete($id)
        // Delete row in table using PDO

In the mapper, I have methods that load with multiple entities and also ones that work on a single one. I like to use the method prefix "find" for methods that will return an array of entities and "load" for methods that return a single entity. This is just a stylistic thing, but I find it makes reading code easier. We then have save and delete methods that allow us to save and remove an entity from the data store.

This is just a skeleton of a specifically written mapper that users PDO. In a ZF2 application I use ZfcBaseMapperAbstractDbMapper and in other applications I tend to abstract the common code into a base class and extend.

Service objects

Lastly, service objects provide the API to the rest of the application:

namespace Book;

use BookMapper;

class Service
    protected $mapper;

    public function __construct(Mapper $mapper)
        $this->mapper = $mapper;

    public function fetchAllByTitle()
        $results = $this->events->trigger(__FUNCTION__.'.pre', $this, array(), 
            function ($result) {
                return is_array($result);
        if ($results->stopped()) { 
            return $results->last(); 

        $books = $this->mapper->fetchAll('title');

        $this->getEventManager()->trigger(__FUNCTION__.'.post', $this, 
            array('books' => $books));

        return $books;

    public function loadById($id)
        $results = $this->events->trigger(__FUNCTION__.'.pre', $this, 
            array('id' => $id), 
            function ($result) {
                return ($result instanceof BookEntity);
        if ($results->stopped()) { 
            return $results->last(); 
        $book = $this->mapper->loadById($id);
        $this->getEventManager()->trigger(__FUNCTION__.'.post', $this, array('book' => $book));

        return $book;

    // etc

A simple service object essentially proxies through to the mapper. I generally have more specific methods, such as fetchAllByTitle(), but that's a personal preference. In this example, I have an ZF2 event manager in play and the service object triggers events as required.

The service object is also useful when there are multiple related objects. For instance, if books had tags that were loaded separately, then I would have a method such as loadTagsIntoBook($book) on this service object. Of course, others prefer to use an ORM, such as Doctrine for these things.


This overview shows the type of methods that I have in each type of core object in my model layer. My controllers and view helpers only ever deal with service objects and entities, so I can change my mapper at any time.

You also need to think carefully where the business logic lives. I'm a fan of putting the logic in the entities as well as in service objects. Others tend to like their entities to be quite "dumb", though.

Missing fields in $_POST

I recently updated to OS X 10.8 (Mountain Lion) which has PHP 5.3.13 installed by default.

When testing something today, I discovered that a very very large form wasn't submitting all fields. It seemed that $_POST was being truncated. After a little bit of searching around I discovered the max_input_vars php.ini setting. This is new since PHP 5.3.9 and defaults to 1000.

As OS X also comes with suhosin installed, if you want to increase the number of fields for _POST you need to set the following in php.ini:

max_input_vars = 2000 = 2000
suhosin.request.max_vars = 2000

Also, ensure that and suhosin.request.max_value_length are large enough.

Update: I was pointed to Supercolliding a PHP array which explains why this setting was introduced.

A primer on PHP namespaces

I know that there are a lot of posts now about namespaces in PHP 5.3. This is mine which is how I learnt how they work.

What are namespaces?

From the PHP manual:

namespaces are a way of encapsulating items

Hardly the most useful of definitions, but it's a starting point! A namespace is a way of grouping code that exists across multiple files without having a naming collision. That is, you can have the same named class in two different places if they are encapsulated within namespaces.

Of course the way that ZF1 solves this problem is with Extra_Long_Classnames which are obviously unique. Namespaces allow us to reference the classname by the important bit (the last section and not have to carry the meta data (where it lives) in every use of the class.

That is, namespaces allow us to:

  • combine libraries with the same classnames
  • avoid very long classnames
  • organise our code easily

Note that namespaces do not just affect classes. They also affect functions and constants.

Defining a namespace

There is a new keyword called namespace which is used to declare a namespace for a file. This is file-wide:

    namespace MyDbStatement;
    class Sqlsrv extends AbstractStatement

Note that we can have multiple sub-namespaces, separated by the backslash. When we extend the Sqlsrv class the extended class is within the namespace too, but not in the same file.

We can then use the class like this:

    $stmt = new MyDbStatementSqlsrv(); 

You can have multiple namespaces in a file, but the first namespace must be the first line of code in the file (except comments and declare statements).

Working within the same namespace

When you are working within the same namespace, then any unqualified functions and classes will be resolved to the current namespace. i.e:

    namespace MyDbStatement;
    function testSqlsrv()
        $stmt = new Sqlsrv();

In this case, the SqlSrv class is in the namespace MyDbStatement.

Namespace importing: the use keyword

We can import a namespace into a different file using the use keyword.

    namespace MyApplication;

    use MyDbStatement;

    $stmt = new StatementSqlsrv();

Note that we don't use a leading in the new statement as we are using a qualified namespace, not a fully-qualified one.

or you can import a specific class from a namespace;

    namespace MyApplication;

    use MyDbStatementSqlsrv;
    $stmt = new Sqlsrv();

It follows you can use multiple use statements:

    use MyDbStatement;
    use MyDbAdapter;
    $stmt = new StatementSqlsrv();
    $adapter = new AdapterSqlsrv();

You cannot do this though:

    use MyDbStatementSqlsrv;
    use MyDbAdapterSqlsrv;
    $stmt = new Sqlsrv();
    $adapter = new Sqlsrv();

as clearly PHP cannot resolve which Sqlsrv class to instantiate.

You can also alias namespaces. This allows us to reference a long namespace with a shorter name or to import two namespaces having the same name and give them different names.

    use MyDbStatementSqlsrv as DbStatement;
    use MyDbAdapterSqlsrv as DbAdapter;
    $stmt = new DbStatement();
    $adapter = new DbAdapter();

This also allows you to write code the focusses on the functionality of the class rather than the specific type. For example, we could start using the Mysqli versions of the statement and adapter by just changing the use statements.

The __NAMESPACE__ constant

The constant __NAMESPACE__ provides the current namespace name. In the global space it will be an empty string.

Namespace resolution

This bit is really important!

An unqualified class name is resolved in this order:

  1. If there is an import statement that aliases another name to this class name, then the alias is applied.
  2. Otherwise the current namespace is applied.

An unqualified function name has different rules:

  1. The current namespace is prepended to the function name.
  2. If the function names doesn't exists in the current namespace, then a global function name is used if it exists.

This means that within a namespace'd file, you can do:

    $date = date('Y-m-d');

but not:

    $datetime = new DateTime();

Instead, you have to use:

    $datetime = new DateTime();


That's really all you need to know to use namespaces in a PHP application. They aren't so hard really.

Setting up PHP & MySQL on OS X 10.7 Lion

With OS X 10.7, Apple continues to ship PHP 5.3 with PEAR, GD and PDO_MYSQL out of the box. This is how to set it up from a clean install of 10.7.


Ensure that the following directories exist:

sudo mkdir /usr/local/include
sudo mkdir /usr/local/bin
sudo mkdir /usr/local/lib
sudo mkdir -p /usr/local/man/man1


  1. Download the 64bit DMG version of MySQL 5.1.x (or 5.5.x) for OS X 10.6 from and install the pkg, the startup item and the pref pane.
  2. Add /usr/local/mysql/bin to the path: vim ~/.bash_profile and add:
    export PATH=~/bin:/usr/local/bin:/usr/local/mysql/bin:$PATH
    export EDITOR=vim

    at top of file. (Note that we set EDITOR whilst we are here so that svn is happy!)

  3. Set up MySQL root password:
    mysqladmin -u root password {new-password}
    mysqladmin -u root -p{new-password} -h localhost password {new-password}
    mysqladmin -u root -p{new-password} reload

    Clear the history file by typing history -c so that {new-password} isn't in plain text on the disk.

  4. Now ensure that the mysql.sock file can be found by PHP:
    1. Ensure that MySQL is running
    2. sudo mkdir /var/mysql
    3. sudo ln -s /tmp/mysql.sock /var/mysql/mysql.sock


  1. cd /etc/apache2
  2. Give write permission the config file to root: sudo chmod u+w httpd.conf
  3. sudo vim httpd.conf
  4. Find #LoadModule php5_module libexec/apache2/ and remove the leading #
  5. Find AllowOverride None within the <Directory "/Library/WebServer/Documents">section and change toAllowOverride All so that .htaccess files will work.
  6. Change permissions back: sudo chmod u-w httpd.conf
  7. Restart Apache by unticking and then ticking again the Web Sharing checkbox in System Preferences -> Sharing
  8. Open Finder and navigate to /Library/WebServer/Documents/ using shift+cmd+g
  9. Create a new folder called "orig" and place all files currently in the Documents folder into it. (note that it will ask for your password as the Documents folder is only writable by root.
  10. Create a new file called info.php with <?php phpinfo(); inside it.
  11. Use Safari to navigate to http://localhost/info.php and check that the PHP version is displayed (5.3.6 at the time of writing).


  1. cd /etc
  2. sudo cp php.ini.default php.ini
  3. sudo chmod ug+w php.ini
  4. sudo chgrp admin php.ini
  5. vim php.ini (assuming your user is a member of the admin group) and change settings appropriately. Change:
    error_reporting  =  E_ALL | E_STRICT
    display_errors = On
    html_errors = On
    extension_dir = "/usr/lib/php/extensions/no-debug-non-zts-20090626"

    (I like to see my xdebug errors in bright orange!)
    Also, change all instances of /var/mysql/mysql.sock to /tmp/mysql.sock


Can't have a PHP development environment without xdebug! Apple appears to agree as Lion ships with it.

  1. vim /etc/php.ini
  2. Find the line:
    and remove the semicolon at the start
  3. If you want to configure your xdebug settings, then scroll to the end of the file and look for the [xdebug] section. I like these settings:
    xdebug.var_display_max_children = 999
    xdebug.var_display_max_data = 99999
    xdebug.var_display_max_depth = 100

    (use with caution…)

  4. Restart apache: sudo apachectl restart and check in the phpinfo that xdebug is now loaded.


We need PEAR! For some reason, it's not set up ready to on Lion, but the install phar file is here, so we just need to run it.

  1. cd /usr/lib/php
  2. sudo php install-pear-nozlib.phar
  3. Edit/etc/php.ini and find the line: ;include_path = ".:/php/includes" and change it to:
    include_path = ".:/usr/lib/php/pear"
  4. sudo pear channel-update
  5. sudo pecl channel-update
  6. sudo pear upgrade-all

PHPUnit and friends

I assume that everyone needs these…

  1. sudo pear channel-discover
  2. sudo pear channel-discover
  3. sudo pear channel-discover
  4. sudo pear install phpunit/PHPUnit
  5. sudo pear install phpunit/phpcpd
  6. sudo pear install PHP_CodeSniffer


A couple of projects I work on use the PECL OAuth component:

  1. Ensure you have installed Xcode from the Mac App Store
  2. Download the latest PCRE source code from and unzip to a folder on your desktop
  3. cd ~/Desktop/pcre-8.12
  4. ./configure
  5. sudo cp pcre.h /usr/include/
  6. Remove the pcre folder on your desktop as you don't need it any more
  7. sudo pecl install oauth
  8. Edit/etc/php.ini add these lines to the end of the file:
  9. Restart apache: sudo apachectl restart and check in the phpinfo that OAuth is now loaded.


This is useful! Follow the installation details by Michale Gracie here:

It all works on this machine, anyway :)

Other options

If you'd rather use a packaged version, then these are two alternatives:

Great PHP developer required in Central Birmingham

Update: This position has now been filled.

So… we seem to be in the same position as everyone else and are looking to hire a new, great, PHP developer!

From the spec:

Big Room Internet are looking for a great PHP software engineer to bring depth to the team and spearhead our future development. You will be working across a range of projects, ideally with experience in seeing through a project from start to finish. You will also interact with customers from taking work requests through to working with them to resolve an issue and get new software approved. You must be self motivated and be able to show that you can work on your own initiative.

Ideally you'll have several years of PHP website experience, but also the knowledge of best practices when working as a team on projects. You will be able to take the lead on technical decisions for projects along with documenting them and proving that they work.

This position is based in our office in central Birmingham.

Obviously, we do a fair amount of our work using Zend Framework :)

If you're interested or if you know someone who is, please get them to contact me :)

Displaying an RSS feed in WordPress

My wife decided that she wanted to display a list of her latest AudioBoos in the sidebar of her blog. She looked at the AudioBoo JavaScript widget but decided it wasn't subtle enough and so she enlisted me to solve her problem.

It turns out that AudioBoo has an RSS feed, so a simple plugin was required. I had a quick look on the extension site, but most are now "widgets" which her theme isn't set up for or didn't provide an unsigned list. Hence, I whipped up a small extension for her.

It turns out that WordPress ships with MagpieRSS baked in, so the work to get the feed is trivial:

include_once(ABSPATH . WPINC . '/rss.php');
$messages = fetch_rss($url);

The rest of the work is simply formatting the output. The key requirement that she had was that it should provide an unsigned list with title, date and optionally the summary. Along with providing some customisation for her, this is what I came up with:

function akrabat_simple_rss($options = array())
$defaults = array(
'url' => '',
'number_of_items' => 5,
'display_date' => true,
'date_format' => 'd M Y at H:i',
'display_summary' => true,
'number_of_summary_chars' => 100,
'link_on_title' => true,
'link_on_date' => false,
'css_class' => 'akrabat-simple-rss',

extract (array_merge($defaults, $options));

$output = '';
if (!empty($url)) {
include_once(ABSPATH . WPINC . '/rss.php');
$messages = fetch_rss($url);
if(count($messages->items) == 0){
return '';

if($number_of_items > count($messages->items)) {
$number_of_items = count($messages->items);

$output = '

    for($i = 0; $i < $number_of_items; $i++){
    $message = $messages->items[$i];

    $link = $message['link'];
    $title = $message['title'];
    $date = null;
    if (isset($message['published'])) {
    $date = $message['published'];
    if (!$date && isset($message['pubdate'])) {
    $date = $message['pubdate'];
    $summary = null;
    if (isset($message['summary'])) {
    $summary = $message['summary'];
    if (!$summary && isset($message['description'])) {
    $summary = $message['description'];

    $output .= "

  • ";
    $title_string = htmlspecialchars($title_string);
    if ($link_on_title) {
    $output .= ''.$title_string.'';
    $output .= '


    if ($date && $display_date) {
    $dateString = date($date_format, strtotime($date));
    if ($link_on_date) {
    $dateString = ''.$dateString.'';
    $output .= '


    if ($summary && $display_summary) {
    $summary_string = substr($summary, 0, $number_of_summary_chars);
    if (count(summary) > $number_of_summary_chars) {
    $summary_string = substr(summary_string, 0, -3) . '...';
    $summary_string = htmlspecialchars($summary_string);
    $output .= '


    $output .= "

  • ";
    $output .= "


return $output;

Maybe it's useful to someone else too, and I've documented it somewhere!

PHP 5.3 is quicker than PHP 5.2

I know that everyone already knows this, but I happened to find out for myself recently!

I was looking at the way view helpers work in ZF2 and thought it would be more convenient if we altered the syntax a little. A side-effect of the change was that we'd have to use call_user_func_array, which is perceived as slow. I thought I'd whip up a simple test to find out how much slower it would be over a direct method call.

That is, how much slower is this code:
$a = new A();
call_user_func_array(array($a, 'b'), array(1));

than this code:
$a = new A();

Bear in mind that I don't do formal benchmarks and have no clue on methodology. Paul Jones is your man for proper testing of performance.

With that caveat out of the way this is the code I wrote to test:



class A
function b($a)

define ('ITERATIONS', 10000000);
$start = microtime(true);
$a = new A();
for ($i = 0; $i < ITERATIONS; ++$i) {
$stop = microtime(true);
echo 'Direct method call: ' . ($stop - $start) . ' seconds' . PHP_EOL;

$start = microtime(true);
for ($i = 0; $i < ITERATIONS; ++$i) {
call_user_func_array(array($a, 'b'), array(1));
$stop = microtime(true);
echo 'call_user_func_array call: ' . ($stop - $start) . ' seconds' . PHP_EOL;

This was the simplest possible scenario I could imagine and so would show call_user_func_array in the best possible light.

I ran the test on my laptop (using the command line) and got these results:

Direct method call: 21.155211925507 seconds
call_user_func_array call: 72.147792100906 seconds

i.e. call_user_func_array is around 3.5 times slower over 10 million iterations.

Nobody uses their laptop to serve a website though! Being the curious sort, I thought I'd test the difference of this script between PHP 5.2 and PHP 5.3 in a completely unscientific way as I'm lazy! I happen to have a server with both PHP 5.2 and PHP 5.3. on it, so I used that:

PHP 5.2:

Direct method call: 8.3424661159515 seconds
call_user_func_array call: 26.904649972916 seconds

PHP 5.3:

Direct method call: 3.35181903839 seconds
call_user_func_array call: 11.5989868641 seconds

As you can see, my server is much faster than my laptop :) Also, the relative difference is the same. i.e. PHP 5.3's call_user_func_array is as slow as it is in PHP 5.2.

I think I'll move everything to PHP 5.3 for the free performance gain! Of course, The question remains as to whether we should avoid call_user_func_array or not.

I would however recommend testing your own apps and see if it makes any difference and please don't take this post as anything other than something interesting I found out!

Some notes on SQL Server blobs with sqlsrv

I recently updated my use of SQL Server with Zend_Db_Adapter_Sqlsrv to use UTF-8 throughout. This turned out to be easy enough:

  • Use ntext, nvarchar types in the database
  • add: resources.db.params.driver_options.CharacterSet = "UTF-8" to your application.ini

I subsequently noticed a problem with storing binary data to a varbinary(max) field. The error was:

An error occurred translating string for input param 2 to UCS-2: No mapping for the Unicode character exists in the target multi-byte code page.


The code looked something like this:

$data['filename'] = 'test.gif';
$data["file_contents"] = $binaryData;

Fortunately, my friend Elizabeth Smith, pointed me in the right direction by suggesting I find out about bindings. So I did some research and it turns out that I just need to use an array for the parameter that I pass into the update() or insert() method of Zend_Db_Adapter_Sqlsrv.

It turns out that all you need to do is change the 'file_contents' element of the array to an array that also specifies the data type. The code I ended up with now looks something like this:

$data['filename'] = 'test.gif';
$data["file_contents"] = $binaryData;
if ($adapter == 'Zend_Db_Adapter_Sqlsrv') {
$data["file_contents"] = array($binaryData, SQLSRV_PARAM_IN,

And all is fine.

The list of constants for the Sql Server Driver for PHP is helpfully available on MSDN and the documentation for sqlsrv_query() is worth reading too!

SqlSrv v2 and long field names

A good proportion of the projects my company undertakes end up on Windows servers with IIS and SQL Server and hence we use the SqlSrv PHP extension from Microsoft. We don't host any of these projects ourselves and leave it up to the client's IT department. This is the main reason that we use a database abstraction layer, Zend_Db, in our case as we can swap the underlying database out with less hassle.

A couple of weeks ago, I came across a problem when installing our app onto the client's server.

It didn't work.

This was a surprise as we have a few Windows Server VMWare installations on which we had tested and they had worked fine. The most obvious differences were that this server was 32bit and that it was using v2 of the SqlSrv extension. As there were a number of differences from our usual fare to the client's install, so it took me a while to (a) build a install that exhibited the problem and (b) reduce the problem to something simple.

The error I was getting is this:

exception 'Zend_Db_Statement_Sqlsrv_Exception' with message
'[Microsoft][SQL Server Native Client 10.0]String data, right truncation'
in C:\Websites\project1\library\Zend\Db\Statement\Sqlsrv.php:237

Googling failed me completely!

I eventually narrowed down the SQL statement that caused the problem:

SELECT email_user_include_form_details FROM "forms"

That's not especially complicated :)

At the point, I yelled for help which came in the form of Brian Swan of Microsoft. There's quite a few helpful MS guys around nowadays if you keep your ear close to the PHP community, which is really handy. Brian helped prove that I wasn't going mad and that there really was a problem.

We determined the problem to be that with v2 of the sqlsrv driver, you cannot have a fieldname longer than 30 characters.

Brian assured me that the team are aware of this and it will be fixed in a subsequent update of the driver. Also, it only affects the sqlsrv driver and not the pdo_sqlsrv driver. Hence, there are two workarounds:

1. Shorten your fieldnames to 30 characters or less
2. Use pdo_sqlsrv

Smart readers will already be commenting that there isn't a Zend_Db_Adapter_Pdo_Sqlsrv and they'd be right, but it isn't hard to write one :)


I cobbled together an adapter that works for me and have put it on github. As it's not in the Zend_ namespace, we have to use an additional key in the application.ini file so that it is loaded:

resources.db.adapter = "Pdo_SqlSrv"
resources.db.params.adapterNamespace = "Akrabat_Db_Adapter" = localhostSQLEXPRESS
resources.db.params.username = testuser
resources.db.params.password = testpassword
resources.db.params.dbname = testdatabase

The key difference from a standard Zend_Db adapter is the use of the resources.db.params.adapterNamespace key which tells the system the full name of the class to load.

My limited testing shows that this adapter works with v2 of pdo_sqlsrv which solves my problem with fieldnames that are longer than 30 characters!

Unfortunately, I found out about this too late for Zend Framework 1.11, so I'll have to look at getting it into Zend Framework 2.0.