<?php
/**
 * Upload class
 * Implements the Iterator and Countable interfaces
 * @author Jérôme Brilland
 * @version 1.1
 * @copyright (c) 2015-2021, Jérôme Brilland
 * @license http://www.gnu.org/licenses/gpl.txt GNU General Public License
 */
class Upload implements Iterator, Countable {
    
    protected static $messages = array(
        UPLOAD_ERR_OK => 'The file was uploaded successfully.',
        UPLOAD_ERR_INI_SIZE => 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
        UPLOAD_ERR_FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
        UPLOAD_ERR_PARTIAL => 'The uploaded file was only partially uploaded.',
        UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
        UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder.',
        UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.',
        UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the file upload.'
        );
    
    /**
     * @access private
     */
    private $position = 0;
    
    /**
     * @access public
     */
    public $allowedMIMETypes = array(); 
    
    /**
     * @access public
     */
    public $allowedExtensions = array();
    
    /**
     * @access public
     */
    public $maxSize = 204800;
    
    /**
     * @access private
     */
    private $name; 
    
	/**
	 * Class constructor
	 * @param string name Field name
	 * @access public
	 */
	public function __construct( $name ) {
	    $this->name = $name;
	}
	 
	 /**
      * @access public
      */
	 public function getSize() {
	     if ( is_array( $_FILES[$this->name]['tmp_name'] ) ) {
	         return filesize( $_FILES[$this->name]['tmp_name'][$this->position] );
	     } else {
	         return filesize( $_FILES[$this->name]['tmp_name'] );
	     }
	 }
	 
	 /**
	  * Returns the MIME content type for the file
      * @access public
      */
	 public function getType() {
	     $finfo = finfo_open( FILEINFO_MIME_TYPE );
	     if ( is_array( $_FILES[$this->name]['tmp_name'] ) ) {
	         $result = finfo_file( $finfo, $_FILES[$this->name]['tmp_name'][$this->position] );
	     } else {
	         $result = finfo_file( $finfo, $_FILES[$this->name]['tmp_name'] );
	     }
	     finfo_close( $finfo );
	     return $result;
	 }
	 
	 /**
	  * Returns the file extension
      * @access public
      */
	 public function getExtension() {
	     return strtolower( substr( strrchr( $this->getFileName(), '.' ), 1 ) );
	 }
	 
	 /**
	  * Returns the file name
	  * @access public
	  */
	 public function getFileName() {
	     if ( is_array( $_FILES[$this->name]['name'] ) ) {
	         return $_FILES[$this->name]['name'][$this->position];
	     } else {
	         return $_FILES[$this->name]['name'];
	     }
	 } 
	 
	 /**
	  * Returns the error code
	  * @access public
	  */
	 public function getErrorCode() {
	     if ( is_array( $_FILES[$this->name]['error'] ) ) {
	         return $_FILES[$this->name]['error'][$this->position];
	     } else {
	         return $_FILES[$this->name]['error'];
	     }
	 } 
	 
	 /**
	  * Whether no file was uploaded
	  * @return boolean True if no file was uploaded
	  * @access public
	  */
	 public function isMissing() {
	     return $this->getErrorCode() == UPLOAD_ERR_NO_FILE;
	 } 
	 
	 /**
	  * Get the size of an uploaded image
	  * @return array
	  * @access public
	  */
	 public function getImageSize() {
	     if ( $this->isImage() ) {
	         if ( is_array( $_FILES[$this->name]['tmp_name'] ) ) {
                 return getimagesize( $_FILES[$this->name]['tmp_name'][$this->position] );
             } else {
                 return getimagesize( $_FILES[$this->name]['tmp_name'] );
             }
	     }
	 } 
	 
	 /**
      * @access public
      * @throws FileTooLargeException if the file size exceeds the limit
      * @throws InvalidFileFormatException if the file format is invalid
      * @throws RuntimeException
      */
	 public function validate() {
	     $code = $this->getErrorCode();	     
	     if ( $code != UPLOAD_ERR_OK && $code != UPLOAD_ERR_NO_FILE ) {
	         throw new RuntimeException( static::$messages[$code] );
	     }
	     
	     if ( !empty( $this->allowedMIMETypes ) && !in_array( $this->getType(), $this->allowedMIMETypes ) ) {
	         throw new InvalidFileFormatException();
	     }
	     if ( !empty( $this->allowedExtensions ) && !in_array( $this->getExtension(), $this->allowedExtensions ) ) {
	         throw new InvalidFileFormatException();
	     }
	     if ( $this->getSize() > $this->maxSize ) {
	         throw new FileTooLargeException();
	     }
	 }
	 
	 /**
	  * @return boolean True if the uploaded file is a valid image
	  * @access public
	  */
	 public function isImage() {
	     return ( in_array( $this->getType(), array( 'image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/x-icon' ) ) );
	 } 
	 
	 /**
	  * @return boolean True if the uploaded file is a valid audio file
	  * @access public
	  */
	 public function isAudio() {
	     return ( in_array( $this->getType(), array( 'audio/mpeg', 'audio/mp4', 'audio/ogg', 'audio/webm', 'audio/wav' ) ) );
	 } 
	 
	 /**
	  * @param string
	  * @return string the sanitized file name
	  * @access public
	  */
	 public function sanitize( $filename ) {
	     return preg_replace( '/[^a-z0-9\.]/', '', strtolower( substr( $filename, 0, 255 ) ) );
	 }
	 
	 /**
	  * Moves an uploaded file from the temporary directory to a permanent location
	  * @return boolean True on success
	  * @access public
	  * @throws RuntimeException
	  */
	 public function move( $directory, $filename = null ) {
	     if ( empty( $filename ) ) {
	         $filename = $this->sanitize( $this->getFileName() );
	     }
	     if ( !is_writable( $directory ) ) {
	         throw new RuntimeException( sprintf( 'Unable to write in the %s directory.', $directory ) );
	     }
	     if ( is_array( $_FILES[$this->name]['tmp_name'] ) ) {
	         return move_uploaded_file( $_FILES[$this->name]['tmp_name'][$this->position], $directory . $filename );
	     } else {
	         return move_uploaded_file( $_FILES[$this->name]['tmp_name'], $directory . $filename );
	     }
	 }
	 
	 /**
      * @access public
      */
	 public function rewind() {
	     $this->position = 0;
	 }
	 
	 /**
      * @access public
      */
	 public function current() {
	     return $this;
	 }
	 
	 /**
      * @access public
      */
	 public function key() {
	     return $this->position;
	 }
	 
	 /**
      * @access public
      */
	 public function next() {
	     $this->position ++;
	 }
	 
	 /**
      * @access public
      */
	 public function valid() {
	     return isset( $_FILES[$this->name]['name'][$this->position] );
	 }
	 
	 /**
	  * Returns the number of files uploaded
      * @access public
      */
     public function count() {
         if ( is_array( $_FILES[$this->name]['name'] ) ) {
             return count( array_filter( $_FILES[$this->name]['name'], function( $value ) { return !empty( $value ); } ) );
         }
     } 
	 
}
?>
