Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 12
CRAP
0.00% covered (danger)
0.00%
0 / 150
iaUploadFile
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 12
4290.00
0.00% covered (danger)
0.00%
0 / 150
 get_result
n/a
0 / 0
1
n/a
0 / 0
 __construct
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 6
 uploadToDir
0.00% covered (danger)
0.00%
0 / 1
72.00
0.00% covered (danger)
0.00%
0 / 25
 fileTypeRegExp
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 4
 fileExtension
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 2
 process
0.00% covered (danger)
0.00%
0 / 1
132.00
0.00% covered (danger)
0.00%
0 / 44
 alternativeName
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 5
 fileError
0.00% covered (danger)
0.00%
0 / 1
380.00
0.00% covered (danger)
0.00%
0 / 34
 putError
0.00% covered (danger)
0.00%
0 / 1
6.00
0.00% covered (danger)
0.00%
0 / 3
 files_normalize
0.00% covered (danger)
0.00%
0 / 1
42.00
0.00% covered (danger)
0.00%
0 / 12
 filename_sanitize
0.00% covered (danger)
0.00%
0 / 1
12.00
0.00% covered (danger)
0.00%
0 / 9
 path_exists
0.00% covered (danger)
0.00%
0 / 1
2.00
0.00% covered (danger)
0.00%
0 / 2
 dirnameNoTrailingSlash
0.00% covered (danger)
0.00%
0 / 1
20.00
0.00% covered (danger)
0.00%
0 / 4
<?php
namespace ia\Lib;
use ia\Util\Str;
/**
 * upload files
 *
 */
/*
https://www.b4x.com/android/forum/threads/how-to-sync-a-remote-db-with-sqlite.21298/
https://stackoverflow.com/search?q=%5Bandroid-sqlite%5D+Sync
https://www.youtube.com/watch?v=sKFLI5FOOHs&list=PLl-K7zZEsYLlP-k-RKFa7RyNPa9_wCH2s&index=4
*** https://stackoverflow.com/questions/14069421/show-an-image-preview-before-upload
**** https://stackoverflow.com/questions/37205438/image-upload-with-preview-and-delete-option-javascript-jquery
start letter
// $arr = ['jpg', 'png', 'gif'] + ['jpg', 'png', 'gif', 'pdf', 'xlsx', 'docx'];print_r($arr); da ok
php settings
    php_value max_input_time         10800
    php_value max_execution_time     10800
    php_value upload_max_filesize    110M
    php_value post_max_size          120M
    php_value memory_limit
    php_value max_file_uploads // maximum number of files that can uploaded in one reques
    <input type="hidden" name="MAX_FILE_SIZE" value="20000">
        MAX_FILE_SIZE item cannot specify a file size greater than the file size that has been set in the upload_max_filesize
//@TODO checar prblm con: max_file_uploads // maximum number of files that can uploaded in one reques
*/
class iaUploadFile {
    public $ACCEPT_IMAGES = ['jpg', 'jpeg', 'png', 'gif'];
    public $ACCEPT_PDF = ['pdf'];
    public $ACCEPT_XLSX = ['xlsx'];
    public $ACCEPT_CSV = ['csv'];
    public $ACCEPT_OFFICE = ['docx','xlsx','pptx'];
    public $ACCEPT_DOCUMENT = ['docx','xlsx','pptx','csv','pdf','doc','xls','ppt','jpg', 'jpeg', 'png', 'gif'];
    protected $allowOverwrite;
    // http://php.net/manual/en/features.file-upload.errors.php
    protected $error_messages = array(
        1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
        2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
        3 => 'The uploaded file was only partially uploaded',
        4 => 'No file was uploaded',
        6 => 'Missing a temporary folder',
        7 => 'Failed to write file to disk',
        8 => 'A PHP extension stopped the file upload',
        'unknown_error' => 'unknown error',
        'accept_file_types' => 'Filetype not allowed',
        'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
        'max_file_size' => 'File is too big',
        'min_file_size' => 'File is too small',
        'max_number_of_files' => 'Maximum number of files exceeded',
        'max_width' => 'Image exceeds maximum width',
        'min_width' => 'Image requires a minimum width',
        'max_height' => 'Image exceeds maximum height',
        'min_height' => 'Image requires a minimum height',
        'abort' => 'File upload aborted',
        'image_resize' => 'Failed to resize image',
    );
    protected $fsWebroot;
    protected $result;
    public function get_result() {return $this->result;}
    public function __construct($fsWebroot=null, $allowOverwrite=false) {
        if($fsWebroot == null) {
            global $gConfig;
            $this->fsWebroot = $this->dirnameNoTrailingSlash($gConfig['FS_WEB_ROOT']); //@TODO
        } else {
            $this->fsWebroot = $this->dirnameNoTrailingSlash($fsWebroot);
        }
        $this->allowOverwrite = $allowOverwrite;
    }
    public function uploadToDir($uploadKeys, $webDir, $fileTypes, $generateUniqueName=false) {
        $this->result=['ok'=>true, 'uploaded'=>0, 'originalName'=>[], 'fileBaseName'=>[], 'webPath'=>[], 'errormsg'=>[]];
        if(empty($_FILES)) {
            return $this->result;
        }
        $webDir = $this->dirnameNoTrailingSlash($webDir);
        $acceptFilesRegExp = $this->fileTypeRegExp($fileTypes);
$this->result['regexp']=$acceptFilesRegExp;
$this->result['uploadKeys']=$uploadKeys;
$this->result['webdir']=$webDir;
$this->result['fs']=$this->fsWebroot;
        if(empty($uploadKeys)) {
            $keys = [];
        }
        if(!is_array($uploadKeys)) {
           $keys = [$uploadKeys];
        } else {
            $keys = array_flip($uploadKeys);
        }
$this->result['keys']=$keys;
        $files = $this->files_normalize();
$this->result['normalized files']=$files;
        foreach($files as $fileKey => $fileData ) {
            if(!empty($keys) && !array_key_exists($fileKey, $keys)) {
                continue;
            }
$this->result['va'][]=$fileKey;
            $iLen = count($fileData);
            for($i=0; $i < $iLen; $i++) {
                $this->process($fileData[$i], $webDir, $acceptFilesRegExp, $generateUniqueName);
            }
        }
        return $this->result;
    }
    protected function fileTypeRegExp($fileTypes) {
        foreach($fileTypes as &$f)
            if($f[0] === '.')
                $f = substr($f, 1);
        return '/\.'.implode('|', $fileTypes).'$/iU';
    }
    protected function fileExtension($fileName) {
        $ipos = strripos($fileName,".");
        return $ipos > 0 ? trim(substr($fileName,-$ipos)) : "";
    }
    protected function process($file, $webDir, $acceptFilesRegExp, $generateUniqueName=false) {
        if($generateUniqueName) {
           $file['newName'] = PushId::generate().'.'.$this->fileExtension($file['name']);
           $file['newName'] = str_replace('-','ia',$file['newName']);
        } else
            $file['newName'] = $this->filename_sanitize($file['name']);
        if($this->fileError($file, $acceptFilesRegExp)) {
            $this->putError($file['name'], "Extension inválida");
            return;
        }
        $path = $this->fsWebroot.'/'.$webDir;
        clearstatcache(true, $path); //@TODO clear only file
       // umask(0644);
        if(!$this->path_exists($path)) {
            if(!mkdir($path,  0665, true)) { // duda group 6 o 4, other 4 o 0?
                $this->putError($file['name'], "Can't create web directory");
                return;
            }
        }
        chmod($path, 0777);
        clearstatcache(true, $path); //@TODO clear only file
        if(!is_dir($path)) {
            $this->putError($file['name'], "Invalid web directory");
            return;
        }
        $fullFileName = $path . '/' . $file['newName'];
        clearstatcache(true, $fullFileName);
        if($this->path_exists($fullFileName)) {
            if(is_dir($fullFileName) ) {
                $this->putError($file['name'], "invalid file name, renombrelo!");
                return;
            }
            if(!$this->allowOverwrite) {
                $alternateName = $this->alternativeName($file['newName'], $path);
                if($alternateName == false) {
                    $this->putError($file['name'], "Ya existe un archivo con ese nombre, renombrelo!");
                    return;
                }
                $file['newName'] = $alternateName;
                $fullFileName = $path . '/' . $file['newName'];
            }
        }
        if(!@move_uploaded_file( $file['tmp_name'] , $fullFileName)) {
            $err = error_get_last();
            $this->putError($file['name'], "Error al copiar al website: $fullFileName  ".
                print_r($err,true));
            return;
        }
        chmod($fullFileName, 0666);
        clearstatcache(true, $fullFileName);
        $this->result['res'][$file['name']]=$file['newName'];
        $this->result['originalName'][] = $file['name'];
        $this->result['fileBaseName'][] = $file['newName'];
        $this->result['webPath'][] = str_replace("\\", "/", $webDir.'/'.$file['newName']);
        $this->result['uploaded']++;
    }
    protected function alternativeName($fileName, $path) {
        foreach([1,2,3,4,5,6,7,8,9,'a','b','c','d','e','f','g','h','i','h','k','l','m','n','o','p',] as $letter) {
            $test = str_replace('.', "$letter.", $fileName);
            if(!$this->path_exists($path . '/' . $test)) {
                return $test;
            }
        }
        return false;
    }
    protected function fileError($file, $acceptFilesRegExp) {
        if(empty($file['name']) && empty($file['error']) || $file['error'] == 4 ) {
            return true;
        }
        $error = $file['error'];
        if(!empty($error)) {
            $this->putError($file['name'], $error);
            return true;
        }
        if(!preg_match($acceptFilesRegExp, $file['name'])) {
            $this->putError($file['name'], 'accept_file_types');
            return true;
        }
        if(empty($file['size'])) {
            $this->putError($file['name'], 'Error de transmisión: No llego el tamaño del archivo');
            return true;
        }
        $file_size = iaLib::units2bytes($file['size']);
        if(!empty($_POST['MAX_FILE_SIZE']) && is_numeric($_POST['MAX_FILE_SIZE']) ) {
            $max_file_size = iaLib::units2bytes($_POST['MAX_FILE_SIZE']);
            if($max_file_size > 0 && $file_size > $max_file_size) {
                $this->putError($file['name'], 'max_file_size');
                return true;
            }
        }
        $post_max_size = iaLib::units2bytes( ini_get('post_max_size') );
        if(!empty($post_max_size) && is_numeric($post_max_size) ) {
            $max_file_size = iaLib::units2bytes($post_max_size);
            if($post_max_size > 0 && $file_size > $post_max_size) {
                $this->putError($file['name'], 'post_max_size');
                return true;
            }
        }
        if(empty($file['name'])) {
            $this->putError($file['name'], 'Error de transmisión: No llego el file name');
            return true;
        }
        if(empty($file['newName']) || strlen($file['newName'] > 250)) {
            $this->putError($file['name'], 'invalid name, muy largo ');
            return true;
        }
        //@TODO newName no sea dir
        if(!is_uploaded_file($file['tmp_name'])) {
            $this->putError($file['name'], 'invalid upload temporary directory');
            return true;
        }
        return false;
    }
    protected function putError($prefix, $error) {
        $this->result['ok'] = false;
        $this->result['errormsg'][] = $prefix.": ".(isset($this->error_messages[$error]) ? $this->error_messages[$error] : $error);
    }
    protected function files_normalize() {
        $files=[];
        foreach($_FILES as $key=>$f) {
            if(!array_key_exists('error',$f)) {
                continue;
            }
            if(!is_array($f['error'])) {
                $files[$key][0] = $f;
                continue;
            }
            $len = count($f['error']);
            for($i=0; $i < $len; $i++) {
                foreach(['name', 'type', 'tmp_name', 'error', 'size'] as $subKey) {
                   $files[$key][$i][$subKey] = $f[$subKey][$i];
                }
            }
        }
        return $files;
    }
    public function filename_sanitize($fileName) {
        if(empty($fileName))
            return '';
        $fileName = Str::strim($fileName);
        $fileName = basename(strtolower( URLify::downcode($fileName, 'latin') ));
        $fileName = str_replace([' ', '-', '/', "\\", PATH_SEPARATOR],"_", $fileName);
        if($fileName[0] == '.') {
            $fileName[0] = '_';
       }
       $fileName = preg_replace('/_{2,}/m','_',$fileName);
       return $fileName;
    }
    protected function path_exists($path) {
        clearstatcache(); // (true, $path);
        return file_exists($path);
    }
    public function dirnameNoTrailingSlash($dir) {
        $lastChar = substr($dir, -1);
        if($lastChar == '/' || $lastChar == DIRECTORY_SEPARATOR || $lastChar == "\\") {
            return substr($dir, 0, -1);
        }
        return $dir;
    }
}