Zend Framework 1.5's Zend_Form component is missing support for the file input element as it is waiting on a file upload component to build upon. We're busy people, so we'll fake it…
This is a super simple example showing how to do file uploads with Zend_Form in Zend Framework 1.5.
I'm building on the Simple Zend_Form Example, so make sure you have read that and that it works before you start this one.
This is what the form looks like:

(Unstyled, as usual!)
This is what we need to do:
The form
The form is an extension of Zend_Form and is stored in app/forms/UploadForm.php and so the class name is forms_UploadForm:
<?php
class forms_UploadForm extends Zend_Form
{
public function __construct($options = null)
{
$this->addElementPrefixPath('App', 'App/');
parent::__construct($options);
$this->setName('upload');
$this->setAttrib('enctype', 'multipart/form-data');
$description = new Zend_Form_Element_Text('description');
$description->setLabel('Description')
->setRequired(true)
->addValidator('NotEmpty');
$file = new App_Form_Element_File('file');
$file->setLabel('File')
->setRequired(true)
->addValidator('NotEmpty');
$submit = new Zend_Form_Element_Submit('submit');
$submit->setLabel('Upload');
$this->addElements(array($description, $file, $submit));
}
}
We are going to create our own validator class within our own library namespace (lib/App) and so we need to tell the form about this using addElementPath(). We then set the name and enctype attribute of the form to allow for files to be uploaded.
The form has two fields: a text field called 'description' and the file upload field called 'file', along with a submit button. As Zend Framework doesn't have it's own file element, we will create our own called App_Form_Element_File.
The file form element
The file element, App_Form_Element_File, is stored in lib/App/Form/Element/File.php and looks like this:
<?php
require_once 'Zend/Form/Element/Xhtml.php';
class App_Form_Element_File extends Zend_Form_Element_Xhtml
{
/**
* Flag indicating whether or not to insert ValidFile validator when element is required
* @var bool
*/
protected $_autoInsertValidFileValidator = true;
/**
* Default view helper to use
* @var string
*/
public $helper = 'formFile';
/**
* Set flag indicating whether a ValidFile validator should be inserted when element is required
*
* @param bool $flag
* @return Zend_Form_Element
*/
public function setAutoInsertValidFileValidator($flag)
{
$this->_autoInsertValidFileValidator = (bool) $flag;
return $this;
}
/**
* Get flag indicating whether a ValidFile validator should be inserted when element is required
*
* @return bool
*/
public function autoInsertValidFileValidator()
{
return $this->_autoInsertValidFileValidator;
}
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];
}
}
// auto insert ValidFile validator
if ($this->isRequired()
&& $this->autoInsertValidFileValidator()
&& !$this->getValidator('ValidFile'))
{
$validators = $this->getValidators();
$validFile = array('validator' => 'ValidFile', 'breakChainOnFailure' => true);
array_unshift($validators, $validFile);
$this->setValidators($validators);
// do not use the automatic NotEmpty Validator as ValidFile replaces it
$this->setAutoInsertNotEmptyValidator(false);
}
return parent::isValid($value, $context);
}
}
Zend Framework already provides a view helper for displaying a file element, formFile, so we set the $helper member variable to 'formFile' so that the correct element is rendered when the form is displayed. We then need to ensure that validation is handled correctly. For all other form elements the value of the field is returned in the POST array. For a file, this is not true as the data is with the $_FILES global array. We could handle this in the controller, but by putting it in the element class, we never have to think about it again. The isValid() member function is used to set the value for an element and also run the validator chain to determine if the value is valid or not.
We override isValid() for the file element to provide two functionalities:
* Set the value to the contents of correct sub-array of the $_FILES array .
* Automatically turn on a custom validator called ValidFile which will check if the upload succeeded.
The file element also has some helper functions (setAutoInsertValidFileValidator and getAutoInsertValidFileValidator) to control the auto-insertion of the ValidFile validator. Note that if we do automatically insert the ValidFile validator, then we turn off Zend_Form_Element's automatic NotEmpty validator as it is redundant.
Once we have set the $value variable correctly and inserted our validator, we call up to the parent's isValid() function which will run the validation chain for us.
The ValidFile validator
The ValidFile validator's full class name is App_Validate_ValidFile and so it's filename is lib/App/Validate/ValidFile.php. This class handles validating the 'error' field within the $value array (which as you'll recall came from $_FILES). This is the code:
<?php
class App_Validate_ValidFile extends Zend_Validate_Abstract
{
const INI_SIZE = 'iniSize'; // Value: 1; The uploaded file exceeds the upload_max_filesize directive in php.ini
const FORM_SIZE = 'formSize'; // Value: 2; The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.
const PARTIAL = 'partial'; // Value: 3; The uploaded file was only partially uploaded.
const NO_FILE = 'noFile'; // Value: 4; No file was uploaded.
const NO_TMP_DIR = 'noTmpDir'; // Value: 6; Missing a temporary folder.
const CANT_WRITE = 'cantWrite'; // Value: 7; Failed to write file to disk.
const EXTENSION = 'extension'; // Value: 8; File upload stopped by extension. Introduced in PHP 5.2.0.
const ERROR = 'error'; // General error for future proofing against new PHP versions
/**
* @var array
*/
protected $_messageTemplates = array(
self::INI_SIZE => "The uploaded file exceeds the upload_max_filesize directive in php.ini",
self::FORM_SIZE => "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form",
self::PARTIAL => "The uploaded file was only partially uploaded",
self::NO_FILE => "No file was uploaded",
self::NO_TMP_DIR => "Missing a temporary folder",
self::CANT_WRITE => "Failed to write file to disk",
self::EXTENSION => "File upload stopped by extension",
self::ERROR => "Unknown upload error"
);
/**
* Defined by Zend_Validate_Interface
*
* Returns true if and only if $value[error] is equal to UPLOAD_ERR_OK.
*
* @note: This validator expects $value to be the array from $_FILES
*
* @param array $value
* @return boolean
*/
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'];
}
$this->_setValue($valueString);
$result = false;
switch($error) {
case UPLOAD_ERR_OK:
$result = true;
break;
case UPLOAD_ERR_INI_SIZE:
$this->_error(self::INI_SIZE);
break;
case UPLOAD_ERR_FORM_SIZE:
$this->_error(self::FORM_SIZE);
break;
case UPLOAD_ERR_PARTIAL:
$this->_error(self::PARTIAL);
break;
case UPLOAD_ERR_NO_FILE:
$this->_error(self::NO_FILE);
break;
case UPLOAD_ERR_NO_TMP_DIR:
$this->_error(self::NO_TMP_DIR);
break;
case UPLOAD_ERR_CANT_WRITE:
$this->_error(self::CANT_WRITE);
break;
case 8: // UPLOAD_ERR_EXTENSION isn't defined in PHP 5.1.4, so use the value
$this->_error(self::EXTENSION);
break;
default:
$this->_error(self::ERROR);
break;
}
return $result;
}
}
Although this class is quite long, not much happens! All the work happens in isValid() where we set $error to the 'error' value originally from the $_FILES array and then set our error message based on which error PHP has reported to us. If PHP reported no error (UPLOAD_ERR_OK), then we return true, otherwise we return false after having set the error value.
The controller
Lastly, the controller is essentially identical to what we did in the Simple Zend_Form Example:
<?php
class IndexController extends Zend_Controller_Action
{
function indexAction()
{
$this->view->pageTitle = "Zend_Form File Upload Example";
$this->view->bodyCopy = "<p>Please fill out this form.</p>";
$form = new forms_UploadForm();
if ($this->_request->isPost()) {
$formData = $this->_request->getPost();
if ($form->isValid($formData)) {
// success - do something with the uploaded file
$uploadedData = $form->getValues();
Zend_Debug::dump($uploadedData, '$uploadedData');
exit;
} else {
$form->populate($formData);
}
}
$this->view->form = $form;
}
}
If it all validates correctly, then you need to process the uploaded data. For this example we just display it using Zend_Debug::dump(), so you can see what it looks like:

Conclusion
As you can see, although Zend Framework 1.5 doesn't have file upload element built in, adding one is not especially hard. In fact, we have more code used to validate that and provide an error message on failure than we do for the upload itself!
As usual, here's a zip file of this project: Zend_Form_FileUpload_Example.zip (It includes Zend Framework 1.5.1 which is why it's 3.9MB big).
Test it out and maybe use it as the basis of your file uploading needs with Zend_Form.