Simple Zend_Form File Upload Example Revisited

16th May 2008

I've been thinking about the Simple Zend_Form File Upload Example that I discussed last month.

To recap, if you haven't read the comments, if the form fails to validate for some reason then you get a nasty error:

Warning: htmlspecialchars() expects parameter 1 to be string, object given in /Users/rob/Sites/akrabat/Zend_Form_FileUpload_Example/lib/Zend/View/Abstract.php on line 786

Essentially, what is happening is that the App_Form_Element_File class that we wrote assigns the $_FILES array to the $value parameter for the form element. On redisplay of the form, the formFile view helper then calls the escape() view helper passing in the $value when rendering the <input> element. The escape() view helper calls htmlspecialchars() which throws the warning about $value not being a string.

*whew!*

What we need is something that's an array when the data is valid, but can also look like a string to htmlspecialchars(). This got me thinking about the SPL and creating an object for the data from the $_FILES array.

Let's call this object App_Form_Element_FileValue and store it in lib/App/Form/Element/FileValue.php:

<?php

class App_Form_Element_FileValue extends ArrayObject
{
    public function __toString()
    {
        $result ";
        if(isset($this->name)) {
            $result $this->name;
        }
        return $result;
    }
}

The ArrayObject class is part of the SPL and handily provides a set of functions that enables the object to work with most functions that we like to use with an array including the ability to access the data using array notation. We implement the PHP5 magic function __toString() so that htmlspecialchars() will get a string from the object when it asks for one which nicely knocks that problem on the head.

To integrate it into the code, we need to modify App_Form_Element_File::isValid() from:


    public function isValid($value$context null)
    {
        // for a file upload, the value is not in the POST array, it's in $_FILES
        $key $this->getName();
        if(null === $value) {
            if(isset($_FILES[$key])) {
                $value $_FILES[$key];
            }
        }
        // continues…

to


    public function isValid($value$context null)
    {
        // for a file upload, the value is not in the POST array, it's in $_FILES
        $key $this->getName();
        if(null === $value) {
            if(isset($_FILES[$key])) {
                $value = new App_Form_Element_FileValue($_FILES[$key]);
            }
        }
        // continues…

We also need to modify the validator App_Validate_ValidFile::isValid() function as it's rather too rigourous in its checking. We currently check that $value is an array using is_array():


    public function isValid($value)
    {
        // default value and error is "no file uploaded"
        $valueString ";
        $error UPLOAD_ERR_NO_FILE;
        
        if(is_array($value) && array_key_exists('error'$value)) {
            // set the error to the correct value
            $error $value['error'];
            
            // set the %value% placeholder to the uplaoded filename
            $valueString $value['name'];
        }
        // continues…

As $value is now an object of type App_Form_Element_FileValue, we need to change the test in the if statement to:


    public function isValid($value)
    {
        // default value and error is "no file uploaded"
        $valueString ";
        $error UPLOAD_ERR_NO_FILE;
        
        if((is_array($value) || $value instanceof ArrayObject) 
            && array_key_exists('error'$value)) {
            // set the error to the correct value
            $error $value['error'];
            
            // set the %value% placeholder to the uplaoded filename
            $valueString $value['name'];
        }
        // continues…

Note that we test for an instance of ArrayObject as that is where the functionality of array behaviour is implemented and is more generic in case we need to reuse this code with another object that behaves like an array.

Those are the only changes needed to elegantly remove the error message.

Here's a zip file of this project with the above changes: Zend_Form_FileUpload_Example_Revisited.zip (It includes Zend Framework 1.5.2 which is why it's 3.9MB big).

Test it out and see if it works for you as well as it works for me !

A review of “Learning PHP Data Objects”

4th May 2008

packt_learning_pdo.png

Packt Publishing recently sent me a couple of books to review. This post is about the second one I received, Learning PHP Data Objects by Dennis Popel. I was excited to receive this book as PDO underlies a lot of the Zend_Db_Adapter objects that I use in my day to day programming. It seemed like a good idea that I should know more about it.

Overview of the book

This book starts out introducing PDO and then takes us on a tour through all its features including error handling and prepared statements along with more advanced features like scrollable cursors.

The first chapter introduces PDO and shows the basics of how to use it to connect to a database, issue a query and retrieve the resultant data. The author also makes the distinction about how the code he is showing is example code and provides pointers to what's missing (such as proper error messages) if you were to use the code in production. I liked this very much as it's important as authors that we realise that inexperienced readers have a tendency to copy/paste examples and use as-is. The introductory chapter closes with a look at prepared statements and so covers all the high points of PDO.

Having introduced the subject, chapter 2 looks in detail at using PDO to connect to a database. This chapter is more tutorial-ish and the code is presented to be typed in. A worked example of a book database is used from here on throughout the rest of the book. The author appears to expect the user to learn what the code does by reading the comments within the code body, as there is little explanation of what the code does in the prose. One thing that's tricky is that when there is explanation after a code block, it's hard to work out which bit of the block the author is referring to. On the whole though, the tutorial nature provides step-by-step progress for people who learn best that way.

Chapter 3 covers error handling. I was pleased to see this important topic given an entire chapter and so early in the book. Error handling is not an after thought here. This chapter provides a good discussion of the types of errors that you will encounter and then provides instructions on how to handle them. Again, the tutorial aspect of the book is emphasised with lots of code to type in. Some of it is in bold, but I'm not sure why as no reference is made to it in the prose. This chapter also starts a dangerous trend where four pages of code is presented (without line numbers) and then the following page or so dissects the code with reference to line numbers that do not exist! This makes the explanation of the code really hard to follow, especially when the you get to the section about lines 189 to 191… This chapter also continues the tutorial by building more pages and showing you how to check for errors along the way.

Chapter 4 studies prepared statements and shows how to use them. Positional and named placeholders are looked at, along with how to insert blobs using bound parameters. In this chapter, the bold sections in the code make more sense as they are referenced in the prose. Again, we have pages of code with no line numbers and then are asked to study lines 60 to 73. The information in this chapter is nevertheless very good and I learnt stuff :)

Having looked at getting data into the application, chapter 5 looks at retrieval and rowsets. This chapter covers counting the number of rows returned and limiting rowsets. It's much shorter as it covers less topics and I'm glad the author didn't pad the chapter just to get the page count up! Chapter 6 is also relatively short, but this time covers a lot of ground. These are advanced topics and include connection attributes, buffered queries, dsn files for connections and transactions. The transactions section gets the most space and is covered quite less, though with rather less prose for the volume of code than I would have liked.

The last chapter in the book is a bit of an odd ball as it looks as designing a model within an MVC application. To me this didn't fit with the specialist PDO nature of the book, and I'd have rather have had more space devoted to transactions or database specific issues.

In summary

This book is an good, detailed tutorial for understanding PDO. It is not a reference book and so relatively hard to dip into to look up a specific thing. If you learn by starting from scratch and working your way through, then this is a very good book. The biggest distraction for me was the long code listings. It would have been better to have either put in line numbers or interspersed the code with the textual explanations.

A review of “Object-Oriented Programming with PHP5″

4th May 2008

packt_oop_with_php5.png
Packt Publishing recently sent me a couple of books to review, so let's start with Object-Oriented Programming with PHP5 by Hasin Hayder. According to the introduction, the book is intended for beginners to intermediate PHP5 programmers and the first chapter has a good introduction to what object oriented programming is and why you would want to use it.

Overview of the book

Chapters two and three of the book are an excellent discussion of how objects work in PHP and cover everything from the use of $this through to object cloning and fluent interfaces. Chapter 4 gives a basic introduction to design patterns, however I feel that it covers too many patterns in not enough detail. It does provide the terminology required though for communicating with other developers about design patterns which will also help when searching the web for more information.

Chapter 5 then introduces reflection and unit testing. This is an odd couple to put together and the entire chapter feels like the author was padding. There are long pages of code with very little explanation of what the code does and no example of real-world usage of the Reflection classes. The unit testing half provides a good introduction to unit testing and shows how to use it. Rather oddly, there's a 10 page table listing all the PHPUnit assert functions which would have been better left to the PHPUnit documentation as the table provides no added value.

Chapter 6 introduces the SPL. Like the design patterns chapter, it covers a lot of objects in relatively shallow depth. Good code examples are provided to show how to use the SPL objects, but again, there's not really enough textual explanation of the code or discussion of real-world usage. Similarly, chapter 7 covers object oriented database access with MySQLi, PDO, ADOdb and MDB2. It finishes up with a couple of pages ADOdb's ActiveRecord object. It's all a bit rushed.

XML is introduced in chapter 8 with SimpleXML and DOM are looked at. Again, a very basic introduction is provided. For example XPath is covered in 3 pages and I still have no idea how to actually use it in a project. The final chapter in the book covers the MVC design pattern as implemented in the author's home-grown framework. As with the rest of the book, a lot of code is presented with little explanation of why the code has been written. For example, the view class presented provides __get(), but not __set(). There is no explanation as to why __get() would be required in a view class, but not __set().

In summary

This book is a whistle-stop tour through object oriented concepts with PHP5 and I'm left with mixed feelings about it. The best parts are chapters 2 and 3 which provide a solid introduction to objects and classes. The rest of the book covers too many disparate topics in very little depth to be useable on its own for those topics.