Skip to content

Instantly share code, notes, and snippets.

@bojovyletoun
Created March 13, 2011 14:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bojovyletoun/868131 to your computer and use it in GitHub Desktop.
Save bojovyletoun/868131 to your computer and use it in GitHub Desktop.
Multiple FileUpload control
<?php
namespace Nette\Forms;
use Nette\Web\HttpUploadedFile;
/** @author bojovyletoun */
class FileUploadMulti extends FileUpload{
public static $garbage = array();
public function __construct($label = NULL){
parent::__construct($label);
$this->setAttribute("multiple");
}
public function setValue($value){
if(is_array($value)){
if(isset($value[0]) && $value[0] instanceof HttpUploadedFile){ //array of files
$this->value = $value;
}elseif(is_string($value[0])){
$this->value = $this->buildForOpera($value); //PHP does't recognize multipart/mixed
}else{
$this->value = array(new HttpUploadedFile()); // single file - standard PHP
}
}elseif($value instanceof HttpUploadedFile){ //single file was uploaded
$this->value = array($value);
}else{
$this->value = array(self::noFile()); // nothing was uploaded
}
return $this;
}
public function getHtmlName(){
return parent::getHtmlName() . '[]';
}
public static function noFile(){
return new HttpUploadedFile(NULL);
}
private function buildForOpera(){
$input = $_POST[$this->name][0]; //HttpRequestFactory damages binary data
$res = preg_match('/^-+([A-Za-z0-9]+)/', substr($input, 0, 200), $boundary); //analyze header => $boundary
if(!$res)
return array(self::noFile());
$chunks = explode($boundary[1], $input); //split to single files, explode is faster
$list = array();
foreach($chunks as $chunk){
$hdr = substr($chunk, 0, 1020); //analyze header => $newline, $disp, $inputname, $name, $type
$res = preg_match(
'#^([\\r\\n]+)Content-Disposition:\s*([^;]+);\s*name=\s*"([^"]+)";\s*filename=\s*"([^"]+)"\s*' .
'Content-Type:\s*([a-z0-9/_-]+)\\1\\1#i',
$hdr, $matches);
if($res){
list($hdr, $newline, $disp, $inputname, $name, $type) = $matches;
$tmp_name = tempnam(ini_get('upload_tmp_dir'), 'upl');
$file = fopen($tmp_name, "wb");
fwrite($file, substr($chunk, strlen($hdr), -strlen($chunks[0] . $newline)));
self::$garbage[] = $tmp_name;
$fileinfo = array(
"tmp_name" => $tmp_name,
"error" => UPLOAD_ERR_OK, // all ok?
"name" => $name,
"type" => $type, //can't set
"size" => filesize($tmp_name),
);
$list[] = new HttpUploadedFile($fileinfo); //HttpUploadedFile
foreach($fileinfo as $k => $v){
$_FILES[$this->name][$k][] = $v; //$_FILES[]
}
}
}
unset($_POST[$this->name]);
if(self::$garbage)
register_shutdown_function(array(__CLASS__, 'garbage'), self::$garbage);
return $list;
}
public static function garbage($list){
foreach($list as $filename){
if(file_exists($filename))
unlink($filename);
}
}
/* validator helper */
public static function map($name, $args){
$files = $args[0]->value;
$res = array();
foreach($files as $httpfile){
$args[0]->value = $httpfile;
$res[] = call_user_func_array(array("parent", $name), $args);
}
$args[0]->setValue($files);
return in_array(FALSE, $res) ? FALSE : TRUE;
}
/* validators */
public function isFilled(){
foreach($this->value as $file){
if(!$file->isOk())
return false;
}
return true;
}
public static function validateFileSize(FileUpload $control, $limit){
return self::map(__FUNCTION__, func_get_args());
}
public static function validateMimeType(FileUpload $control, $mimeType){
return self::map(__FUNCTION__, func_get_args());
}
public static function validateImage(FileUpload $control){
return self::map(__FUNCTION__, func_get_args());
}
}
/**
* Adds control that allows the user to upload files. Multiple.
* @param string control name
* @param string label
* @return FileUpload
*/
public function addFileMulti($name, $label = NULL){
$control = new FileUploadMulti($label);
return $this[$name] = $control;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment