null, 'use_upload_name' => false, 'use_upload_extension' => false, 'overwrite' => false, 'randomize' => false, ); /** * Store already filtered values, so we can filter multiple * times the same file without being block by move_uploaded_file * internal checks * * @var array */ protected $alreadyFiltered = array(); /** * Constructor * * @param array|string $targetOrOptions The target file path or an options array */ public function __construct($targetOrOptions) { if (is_array($targetOrOptions)) { $this->setOptions($targetOrOptions); } else { $this->setTarget($targetOrOptions); } } /** * @param string $target Target file path or directory * @return self */ public function setTarget($target) { if (!is_string($target)) { throw new Exception\InvalidArgumentException( 'Invalid target, must be a string' ); } $this->options['target'] = $target; return $this; } /** * @return string Target file path or directory */ public function getTarget() { return $this->options['target']; } /** * @param bool $flag When true, this filter will use the $_FILES['name'] * as the target filename. * Otherwise, it uses the default 'target' rules. * @return self */ public function setUseUploadName($flag = true) { $this->options['use_upload_name'] = (bool) $flag; return $this; } /** * @return bool */ public function getUseUploadName() { return $this->options['use_upload_name']; } /** * @param bool $flag When true, this filter will use the original file * extension for the target filename * @return self */ public function setUseUploadExtension($flag = true) { $this->options['use_upload_extension'] = (bool) $flag; return $this; } /** * @return bool */ public function getUseUploadExtension() { return $this->options['use_upload_extension']; } /** * @param bool $flag Shall existing files be overwritten? * @return self */ public function setOverwrite($flag = true) { $this->options['overwrite'] = (bool) $flag; return $this; } /** * @return bool */ public function getOverwrite() { return $this->options['overwrite']; } /** * @param bool $flag Shall target files have a random postfix attached? * @return self */ public function setRandomize($flag = true) { $this->options['randomize'] = (bool) $flag; return $this; } /** * @return bool */ public function getRandomize() { return $this->options['randomize']; } /** * Defined by Zend\Filter\Filter * * Renames the file $value to the new name set before * Returns the file $value, removing all but digit characters * * @param string|array $value Full path of file to change or $_FILES data array * @throws Exception\RuntimeException * @return string|array The new filename which has been set, or false when there were errors */ public function filter($value) { // An uploaded file? Retrieve the 'tmp_name' $isFileUpload = (is_array($value) && isset($value['tmp_name'])); if ($isFileUpload) { $uploadData = $value; $sourceFile = $value['tmp_name']; } else { $uploadData = array( 'tmp_name' => $value, 'name' => $value, ); $sourceFile = $value; } if (isset($this->alreadyFiltered[$sourceFile])) { return $this->alreadyFiltered[$sourceFile]; } $targetFile = $this->getFinalTarget($uploadData); if (!file_exists($sourceFile) || $sourceFile == $targetFile) { return $value; } $this->checkFileExists($targetFile); $this->moveUploadedFile($sourceFile, $targetFile); $return = $targetFile; if ($isFileUpload) { $return = $uploadData; $return['tmp_name'] = $targetFile; } $this->alreadyFiltered[$sourceFile] = $return; return $return; } /** * @param string $sourceFile Source file path * @param string $targetFile Target file path * @throws Exception\RuntimeException * @return bool */ protected function moveUploadedFile($sourceFile, $targetFile) { ErrorHandler::start(); $result = move_uploaded_file($sourceFile, $targetFile); $warningException = ErrorHandler::stop(); if (!$result || null !== $warningException) { throw new Exception\RuntimeException( sprintf("File '%s' could not be renamed. An error occurred while processing the file.", $sourceFile), 0, $warningException ); } return $result; } /** * @param string $targetFile Target file path * @throws Exception\InvalidArgumentException */ protected function checkFileExists($targetFile) { if (file_exists($targetFile)) { if ($this->getOverwrite()) { unlink($targetFile); } else { throw new Exception\InvalidArgumentException( sprintf("File '%s' could not be renamed. It already exists.", $targetFile) ); } } } /** * @param array $uploadData $_FILES array * @return string */ protected function getFinalTarget($uploadData) { $source = $uploadData['tmp_name']; $target = $this->getTarget(); if (!isset($target) || $target == '*') { $target = $source; } // Get the target directory if (is_dir($target)) { $targetDir = $target; $last = $target[strlen($target) - 1]; if (($last != '/') && ($last != '\\')) { $targetDir .= DIRECTORY_SEPARATOR; } } else { $info = pathinfo($target); $targetDir = $info['dirname'] . DIRECTORY_SEPARATOR; } // Get the target filename if ($this->getUseUploadName()) { $targetFile = basename($uploadData['name']); } elseif (!is_dir($target)) { $targetFile = basename($target); if ($this->getUseUploadExtension() && !$this->getRandomize()) { $targetInfo = pathinfo($targetFile); $sourceinfo = pathinfo($uploadData['name']); if (isset($sourceinfo['extension'])) { $targetFile = $targetInfo['filename'] . '.' . $sourceinfo['extension']; } } } else { $targetFile = basename($source); } if ($this->getRandomize()) { $targetFile = $this->applyRandomToFilename($uploadData['name'], $targetFile); } return $targetDir . $targetFile; } /** * @param string $source * @param string $filename * @return string */ protected function applyRandomToFilename($source, $filename) { $info = pathinfo($filename); $filename = $info['filename'] . uniqid('_'); $sourceinfo = pathinfo($source); $extension = ''; if ($this->getUseUploadExtension() === true && isset($sourceinfo['extension'])) { $extension .= '.' . $sourceinfo['extension']; } elseif (isset($info['extension'])) { $extension .= '.' . $info['extension']; } return $filename . $extension; } }