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. 2 Responses 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

  3. I’ve just tried this and your previous post, but the image.php (resp. artwork.php) model file doesn’t even get included let alone used for validation. Is there a crucial step you’ve left out?

    By Ben on Jan 25, 2009

Post a Comment