Validating Image Uploads in CakePHP

16th June, 2008 – 10:17 pm

Following on from my article on CakePHP - Uploaded File Validation in Models, today’s snippet will show you how to use Cake’s validation rules to reject invalid images, or images which do not conform to a specified mime-type. This code relies on the fact that you have LibGD installed on your webserver.

<?php
Class Image extends AppModel
{
        var $name = ‘Image’;
        var $validate = array
        (
                ‘uploaded_image’ => array
                (
                        // Ensure file uploaded OK.
                        ‘valid_upload’ => array
                        (
                                ‘rule’ => array(‘validateUploadedfile’, false),
                                ‘message’ => ‘An error occured whilst uploading your image, please try again’,
                        ),
                       
                        // Check image is valid / of allowed mime-type
                        ‘valid_image’ => array
                        (
                                ‘rule’ => array(‘isValidImageFile’),
                                ‘message’ => ‘The file you have uploaded is not a valid or is unsupported, please try again’,
                        ),
                ),
        );

        /**
         * Custom validation rule for uploaded files.
         *
         *  @param Array $data CakePHP File info.
         *  @param Boolean $required Is this field required?
         *  @return Boolean
        */

        function validateUploadedFile($data, $required = false)
        {
                // Remove first level of Array ($data['Image']['size'] becomes $data['size'])
                $upload_info = array_shift($data);

                // No file uploaded.
                if ($required && $upload_info[’size’] == 0) {
                        return false;
                }

                // Check for Basic PHP file errors.
                if ($upload_info[‘error’] !== 0) {
                        return false;
                }

                // Finally, use PHP’s own file validation method.
                return is_uploaded_file($upload_info[‘tmp_name’]);
        }

    /**
     * Use LibGD to determine if an uploaded file is a valid Image by
     * running it’s mime-type against a list of $valid_mime_types
     *
     *  @param Array $data CakePHP File info
     *  @return Boolean
    */

        function isValidImageFile($data)
    {
        // Allow these image mime-types, all others will be rejected
        $valid_mime_types = array(‘image/jpeg’, ‘image/png’, ‘image/gif’);
       
        $data = array_shift($data);
        $filename = $data[‘tmp_name’];
       
        // Catch I/O Errors.
        if (!is_readable($filename)) {
                debug(__METHOD__." failed to read input file: {$filename}");
                return false;
        }
       
        // Retrieve the MimeType of Image, if none is returned, it’s invalid
        if (!$mime_type = $this->getImageMimeType($filename)) {
                debug(__METHOD__." Uploaded file does not have a mime-type");
                return false;
        }
       
        // Check the MimeType against the array of valid ones specified above
        if (!in_array($mime_type, $valid_mime_types)) {
                debug(__METHOD__." Uploaded image has rejected Mime Type: {$mime_type}");
                return false;
        }
       
        if (!$this->__getImageHandleFromFile($filename)) {
                return false;
        }
       
        return true;
    }
   
   
    /**
     * Use LibGD to return an uploaded Image’s MimeType as a String, FALSE
     * on errors or if the file is not an image
     *
     *  @param String $filename Absolute path to file on disc
     *  @return String Image MimeType of $filename, false on failure
    */

    function getImageMimeType($filename)
    {
                // If this error is thrown LibGD is not installed on your server.
        if (!function_exists(‘getimagesize’)) {
                debug(__METHOD__." LibGD PHP Extension was not found, please refer to http://www.php.net/manual/en/book.image.php");
                exit();
        }               
       
        $result = getimagesize($filename);             
        if (isset($result[‘mime’])) {
                return $result[‘mime’];
        }
        return false;
    }
   
   
    /**
     * Returns a LibGD Image Handle for a file specified by $filename
     *
     *  @param String $filename Absolute path to image on disk
     *  @return LibGD Image Handle on success, FALSE on failure.
    */

    function __getImageHandleFromFile($filename)
    {
        if (!is_readable($filename)) {
                debug(__METHOD__." failed to read input file: {$filename}");
                return false;
        }
       
        // Retrieve the MimeType of Image, if none is returned, it’s invalid
        if (!$mime_type = $this->getImageMimeType($filename)) {
                debug(__METHOD__." failed to assertain MimeType of {$filename}");
                return false;
        }
       
        switch ($mime_type)
        {
                case ‘image/jpeg’:
                        $handle = @imagecreatefromjpeg($filename);
                        break;
                       
                case ‘image/gif’:
                        $handle = @imagecreatefromgif($filename);
                        break;
                       
                case ‘image/png’:
                        $handle = @imagecreatefrompng($filename);
                        break;
                       
                default:
                        debug(__METHOD__." Didn’t know how to handle MimeType: {$mime_type}");
                        $handle = false;
                        break;
        }
       
        return $handle;
    }
}
 

As always, comments are welcome.

  1. One Response to “Validating Image Uploads in CakePHP”

  2. Just a comment after reading Chris Shiflett’s article on file uploads. You probably shouldn’t rely on $upload_info['size'] for the file size as this is transmitted from the client. Checking the file size yourself with the filesize() function is better.

    By John on Aug 26, 2008

Post a Comment