Take the 2-minute tour ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

I know there are many PHP upload classes out there, but I'm comfortable with my own code, so I made a simple one here.

What do you think of my approach when isImage is true?

Of course a general opinion about it and any feedback will help.

<?php


/**
* This will allow easy handling of a php upload 
* @author: Mihai Ionut Vilcu ([email protected])
* 2-July-2013
*/
class Upload
{

    var $settings = array(
        'folder' => '.', // the folder where the images will be placed
        'isImage' => 0, // if true it will treat files as images
        'maxSize' => 2, // the max allowed size in MB
        'allowed_extensions' => array(), // an array of lowercase allowed extensions, (!) IF EMPTY ALL ARE ALLOWED (!)
        'overwrite' => 0 // if true it will overwrite the file on the server in case it has the same name
        );

    var $errors = array(); // will hold the errors
    var $success = array(); // will hold the success messages
    var $allowed_chars = "a-z0-9_.-"; // allowed chars in a file name, case insensitive

    function __construct($settings = array()) {
        // we update the settings
        $this->updateSettings($settings);
    }

    /**
     * Will process the files
     * @param  string $inputName the name of the input to be checked
     * @param  array  $settings  settings
     * @return array/string            uploaded file(s) name
     */
    function upload($inputName = 'file', $settings = array()) {
        // we update the settings
        $this->updateSettings($settings);

        if(!isset($_FILES[$inputName])) // if we have no file we have nothing to do
            return false;



        if(is_array($inputName) || is_object($inputName)) { // multiple input names
            $result = array();

            foreach ($inputName as $file) 
                $result[] = $this->handleFiles($file);

            return $result;
        } else { // single input name
            return $this->handleFiles($_FILES[$inputName]);
        }

    }

    /**
     * Will handle the files and perform validations
     * @param  array $files the array of the files from $_FILES
     * @return array        array with the uploaded files
     */
    function handleFiles($files) {
        $files = $this->reArrayFiles($files);

        if(!is_writable($this->settings['folder'])) {
            $this->errors[] = array($this->settings['folder'], " This folder is not writable !");
            return false;
        }

        foreach ($files as $file) {
            $result = array();
            $file['name'] = $this->filterFilename($file['name']);

            // if no filename nothing to do
            if(trim($file['name']) == '')
                continue;

            if($file['error'] > 0) {
                $this->errors[] = array($file['name'], $this->codeToMessage($file['error']));
                continue;
            }
            // we check the file size
            if($file['size'] > $this->settings['maxSize'] * 1024 * 1024) {
                $this->errors[] = array($file['name'], "The size of the file exceeds the allowed limit (".$this->settings['maxSize']."MB).");
                continue;
            }

            // check the extension, remember if settings allowed_extensions is empty it will return allow all of them
            $info = pathinfo($file['name']);
            $info['extension'] = isset($info['extension']) ? $info['extension'] : ''; // in case the file name has no extension

            if(!empty($this->settings['allowed_extensions']) && !in_array(strtolower($info['extension']), $this->settings['allowed_extensions'])) {
                $this->errors[] = array($file['name'], "This extension is not allowed !");
                continue;
            }

            // we build the path for upload
            $upload_path = ltrim($this->settings['folder'], DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR.$file['name'];

            // check if the file exists on the server
            if(!$this->settings['overwrite'] && file_exists($upload_path)){
                $this->errors[] = array($file['name'], "This file already exists, rename it !");
                continue;
            }


            if($this->settings['isImage']) { // we need to handle it as an image

                if($img = $this->imagecreatefromfile($file['tmp_name'], $function)) {
                    if($function($img, $upload_path)) { // we pass the image through a filter
                        $this->success[] = array($file['name'], "It was uploaded successfully !");
                        $result[] = $file['name'];
                    }

                    // remove uploaded file
                    @unlink($file['tmp_name']);
                } else 
                    $this->errors[] = array($file['name'], "This file is not a valid image !");


            } else { // we treat it as a normal file
                if(move_uploaded_file($file['tmp_name'], $upload_path)) {
                    $this->success[] = array($file['name'], "It was uploaded successfully !");
                    $result[] = $file['name'];
                }
            }

        }
        return $result;
    }

    /**
     * it will rearrange the array with the info about the files generated in $_FILES 
     * @author: http://www.php.net/manual/en/features.file-upload.multiple.php#53240
     * @edited: Mihai Ionut Vilcu (it will handle one file also)
     * @param  array $file_post the $_FILES array
     * @return array the new array            
     */
    function reArrayFiles(&$file_post) {

        $file_ary = array();

        if(!is_array($file_post['name']))
            return array($file_post);

        $file_count = count($file_post['name']);
        $file_keys = array_keys($file_post);

        for ($i=0; $i<$file_count; $i++) {
            foreach ($file_keys as $key) {
                $file_ary[$i][$key] = $file_post[$key][$i];
            }
        }

        return $file_ary;
    }

    /**
     * generates the html code for a basic upload form, it can generate the input fields only or the compleate form
     * @param  integer $number        the number of inputs
     * @param  string  $name          the name of the input(s)
     * @param  integer $complete_form if true it will generate the compleate forms insetead of just input fields
     * @param  string  $location      location where the form will send the data(in case the form is compleate)
     * @param  array   $extra         extra attributes for input(s)
     * @param  array   $extra_form    extra attributes for form
     * @return string                 html code generated
     */
    function generateInput($number = 1, $name = 'file', $complete_form = 0, $location = '?', $extra = array(), $extra_form = array()) {

        $html = $attr = $attr_form = "";

        foreach ($extra as $key => $value)
            $attr .= " $key = '$value' ";
        foreach ($extra_form as $key => $value)
            $attr_form .= " $key = '$value' ";


        for($i = 0; $i < $number; $i++)
            $html .= "File ".($i+1)." 
            <input type='file' name='$name".($number > 1 ? "[]" : "")."'$attr>
            <br/>
            ";

        if($complete_form == 1)
            $html = "<form action='$location' method='post' enctype='multipart/form-data' $attr_form>
            ".$html."
            <input type='submit' value='Upload'>\n</form>";

        return $html;

    }


    /**
     * gets the max file size for upload allowed on the server in MB
     * @return integer the max size in MB
     */
    function getMaxUpload() {
        $max_upload = (int)(ini_get('upload_max_filesize'));
        $max_post = (int)(ini_get('post_max_size'));
        $memory_limit = (int)(ini_get('memory_limit'));
        return min($max_upload, $max_post, $memory_limit);
    }

    /**
     * makes sure that the file name only contains allowed chars
     * @param  string $filename file name
     * @return string           filtered file name
     */
    function filterFilename($filename) {
        return preg_replace("/[^$this->allowed_chars]/i", "_", $filename);
    }
    /**
     * it will interpret the file upload error codes
     * @param  integer $code the error code
     * @return string       error message
     */
    function codeToMessage($code) { 
        switch ($code) { 
            case UPLOAD_ERR_INI_SIZE: 
                $message = "The uploaded file exceeds the upload_max_filesize directive in php.ini"; 
                break; 
            case UPLOAD_ERR_FORM_SIZE: 
                $message = "The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form"; 
                break; 
            case UPLOAD_ERR_PARTIAL: 
                $message = "The uploaded file was only partially uploaded"; 
                break; 
            case UPLOAD_ERR_NO_FILE: 
                $message = "No file was uploaded"; 
                break; 
            case UPLOAD_ERR_NO_TMP_DIR: 
                $message = "Missing a temporary folder"; 
                break; 
            case UPLOAD_ERR_CANT_WRITE: 
                $message = "Failed to write file to disk"; 
                break; 
            case UPLOAD_ERR_EXTENSION: 
                $message = "File upload stopped by extension"; 
                break; 

            default: 
                $message = "Unknown upload error"; 
                break; 
        } 
        return $message; 
    }   

    /**
     * makes sure that the settings are updated and correct
     * @param  array $settings new settings
     * @return void           
     */
    function updateSettings($settings) {
        $this->settings = array_merge($this->settings, $settings);
        $this->settings['maxSize'] = min($this->settings['maxSize'], $this->getMaxUpload());
    }


    /**
     * will create an image from a file
     * @credits: http://www.php.net/manual/en/function.imagecreate.php#81831
     * @edited: Mihai Ionut Vilcu ([email protected]) - added $fun 
     * @param  string  $path           path to the file
     * @param  string $fun it will hold the function required for adding image data in the file
     * @param  boolean $user_functions if true you need to have defined a function imagecreatefrombmp you can find one http://www.php.net/manual/en/function.imagecreatefromwbmp.php#86214
     * @return resource/false                  false if it fails
     */
    function imagecreatefromfile($path, &$fun, $user_functions = false)
    {
        $info = @getimagesize($path);

        if(!$info)
        {
            return false;
        }

        $functions = array(
            IMAGETYPE_GIF => 'imagecreatefromgif',
            IMAGETYPE_JPEG => 'imagecreatefromjpeg',
            IMAGETYPE_PNG => 'imagecreatefrompng',
            IMAGETYPE_WBMP => 'imagecreatefromwbmp',
            IMAGETYPE_XBM => 'imagecreatefromwxbm',
            );

        if($user_functions)
        {
            $functions[IMAGETYPE_BMP] = 'imagecreatefrombmp';
        }

        if(!$functions[$info[2]])
        {
            return false;
        }

        if(!function_exists($functions[$info[2]]))
        {
            return false;
        }
        $fun = str_replace("createfrom", "", $functions[$info[2]]);

        $targetImage = $functions[$info[2]]($path);
        // fix for png transparency
        imagealphablending( $targetImage, false );
        imagesavealpha( $targetImage, true );
        return $targetImage;
    }   

}

As a side note, you can also find it on github here.

share|improve this question
add comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.