diff --git a/src/Transfer/Adapter/AbstractAdapter.php b/src/Transfer/Adapter/AbstractAdapter.php new file mode 100644 index 0000000..a699c81 --- /dev/null +++ b/src/Transfer/Adapter/AbstractAdapter.php @@ -0,0 +1,1508 @@ + array( - Form is the name within the form or, if not set the filename + * name, - Original name of this file + * type, - Mime type of this file + * size, - Filesize in bytes + * tmp_name, - Internally temporary filename for uploaded files + * error, - Error which has occurred + * destination, - New destination for this file + * validators, - Set validator names for this file + * files - Set file names for this file + * )) + * + * @var array + */ + protected $files = array(); + + /** + * TMP directory + * @var string + */ + protected $tmpDir; + + /** + * Available options for file transfers + */ + protected $options = array( + 'ignoreNoFile' => false, + 'useByteString' => true, + 'magicFile' => null, + 'detectInfos' => true, + ); + + /** + * Send file + * + * @param mixed $options + * @return bool + */ + abstract public function send($options = null); + + /** + * Receive file + * + * @param mixed $options + * @return bool + */ + abstract public function receive($options = null); + + /** + * Is file sent? + * + * @param array|string|null $files + * @return bool + */ + abstract public function isSent($files = null); + + /** + * Is file received? + * + * @param array|string|null $files + * @return bool + */ + abstract public function isReceived($files = null); + + /** + * Has a file been uploaded ? + * + * @param array|string|null $files + * @return bool + */ + abstract public function isUploaded($files = null); + + /** + * Has the file been filtered ? + * + * @param array|string|null $files + * @return bool + */ + abstract public function isFiltered($files = null); + + /** + * Adds one or more files + * + * @param string|array $file File to add + * @param string|array $validator Validators to use for this file, must be set before + * @param string|array $filter Filters to use for this file, must be set before + * @return AbstractAdapter + * @throws Exception Not implemented + */ + //abstract public function addFile($file, $validator = null, $filter = null); + + /** + * Returns all set types + * + * @return array List of set types + * @throws Exception Not implemented + */ + //abstract public function getType(); + + /** + * Adds one or more type of files + * + * @param string|array $type Type of files to add + * @param string|array $validator Validators to use for this file, must be set before + * @param string|array $filter Filters to use for this file, must be set before + * @return AbstractAdapter + * @throws Exception Not implemented + */ + //abstract public function addType($type, $validator = null, $filter = null); + + /** + * Returns all set files + * + * @return array List of set files + */ + //abstract public function getFile(); + + /** + * Set the filter plugin manager instance + * + * @param FilterPluginManager $filterManager + * @return AbstractAdapter + */ + public function setFilterManager(FilterPluginManager $filterManager) + { + $this->filterManager = $filterManager; + return $this; + } + + /** + * Get the filter plugin manager instance + * + * @return FilterPluginManager + */ + public function getFilterManager() + { + if (!$this->filterManager instanceof FilterPluginManager) { + $this->setFilterManager(new FilterPluginManager()); + } + return $this->filterManager; + } + + /** + * Set the validator plugin manager instance + * + * @param ValidatorPluginManager $validatorManager + * @return AbstractAdapter + */ + public function setValidatorManager(ValidatorPluginManager $validatorManager) + { + $this->validatorManager = $validatorManager; + return $this; + } + + /** + * Get the validator plugin manager instance + * + * @return ValidatorPluginManager + */ + public function getValidatorManager() + { + if (!$this->validatorManager instanceof ValidatorPluginManager) { + $this->setValidatorManager(new ValidatorPluginManager()); + } + return $this->validatorManager; + } + + /** + * Adds a new validator for this class + * + * @param string|Validator\ValidatorInterface $validator Type of validator to add + * @param bool $breakChainOnFailure If the validation chain should stop an failure + * @param string|array $options Options to set for the validator + * @param string|array $files Files to limit this validator to + * @return AbstractAdapter + * @throws Exception\InvalidArgumentException for invalid type + */ + public function addValidator($validator, $breakChainOnFailure = false, $options = null, $files = null) + { + if (is_string($validator)) { + $validator = $this->getValidatorManager()->get($validator, $options); + if (is_array($options) && isset($options['messages'])) { + if (is_array($options['messages'])) { + $validator->setMessages($options['messages']); + } elseif (is_string($options['messages'])) { + $validator->setMessage($options['messages']); + } + + unset($options['messages']); + } + } + + if (!$validator instanceof Validator\ValidatorInterface) { + throw new Exception\InvalidArgumentException( + 'Invalid validator provided to addValidator; ' . + 'must be string or Zend\Validator\ValidatorInterface' + ); + } + + $name = get_class($validator); + + $this->validators[$name] = $validator; + $this->break[$name] = $breakChainOnFailure; + $files = $this->getFiles($files, true, true); + foreach ($files as $file) { + if ($name == 'NotEmpty') { + $temp = $this->files[$file]['validators']; + $this->files[$file]['validators'] = array($name); + $this->files[$file]['validators'] += $temp; + } else { + $this->files[$file]['validators'][] = $name; + } + + $this->files[$file]['validated'] = false; + } + + return $this; + } + + /** + * Add Multiple validators at once + * + * @param array $validators + * @param string|array $files + * @return AbstractAdapter + * @throws Exception\InvalidArgumentException for invalid type + */ + public function addValidators(array $validators, $files = null) + { + foreach ($validators as $name => $validatorInfo) { + if ($validatorInfo instanceof Validator\ValidatorInterface) { + $this->addValidator($validatorInfo, null, null, $files); + } elseif (is_string($validatorInfo)) { + if (!is_int($name)) { + $this->addValidator($name, null, $validatorInfo, $files); + } else { + $this->addValidator($validatorInfo, null, null, $files); + } + } elseif (is_array($validatorInfo)) { + $argc = count($validatorInfo); + $breakChainOnFailure = false; + $options = array(); + if (isset($validatorInfo['validator'])) { + $validator = $validatorInfo['validator']; + if (isset($validatorInfo['breakChainOnFailure'])) { + $breakChainOnFailure = $validatorInfo['breakChainOnFailure']; + } + + if (isset($validatorInfo['options'])) { + $options = $validatorInfo['options']; + } + + $this->addValidator($validator, $breakChainOnFailure, $options, $files); + } else { + if (is_string($name)) { + $validator = $name; + $options = $validatorInfo; + $this->addValidator($validator, $breakChainOnFailure, $options, $files); + } else { + $file = $files; + switch (true) { + case (0 == $argc): + break; + case (1 <= $argc): + $validator = array_shift($validatorInfo); + case (2 <= $argc): + $breakChainOnFailure = array_shift($validatorInfo); + case (3 <= $argc): + $options = array_shift($validatorInfo); + case (4 <= $argc): + if (!empty($validatorInfo)) { + $file = array_shift($validatorInfo); + } + default: + $this->addValidator($validator, $breakChainOnFailure, $options, $file); + break; + } + } + } + } else { + throw new Exception\InvalidArgumentException('Invalid validator passed to addValidators()'); + } + } + + return $this; + } + + /** + * Sets a validator for the class, erasing all previous set + * + * @param array $validators Validators to set + * @param string|array $files Files to limit this validator to + * @return AbstractAdapter + */ + public function setValidators(array $validators, $files = null) + { + $this->clearValidators(); + return $this->addValidators($validators, $files); + } + + /** + * Determine if a given validator has already been registered + * + * @param string $name + * @return bool + */ + public function hasValidator($name) + { + return (false !== $this->getValidatorIdentifier($name)); + } + + /** + * Retrieve individual validator + * + * @param string $name + * @return Validator\ValidatorInterface|null + */ + public function getValidator($name) + { + if (false === ($identifier = $this->getValidatorIdentifier($name))) { + return null; + } + return $this->validators[$identifier]; + } + + /** + * Returns all set validators + * + * @param string|array $files (Optional) Returns the validator for this files + * @return null|array List of set validators + */ + public function getValidators($files = null) + { + if ($files == null) { + return $this->validators; + } + + $files = $this->getFiles($files, true, true); + $validators = array(); + foreach ($files as $file) { + if (!empty($this->files[$file]['validators'])) { + $validators += $this->files[$file]['validators']; + } + } + + $validators = array_unique($validators); + $result = array(); + foreach ($validators as $validator) { + $result[$validator] = $this->validators[$validator]; + } + + return $result; + } + + /** + * Remove an individual validator + * + * @param string $name + * @return AbstractAdapter + */ + public function removeValidator($name) + { + if (false === ($key = $this->getValidatorIdentifier($name))) { + return $this; + } + + unset($this->validators[$key]); + foreach (array_keys($this->files) as $file) { + if (empty($this->files[$file]['validators'])) { + continue; + } + + $index = array_search($key, $this->files[$file]['validators']); + if ($index === false) { + continue; + } + + unset($this->files[$file]['validators'][$index]); + $this->files[$file]['validated'] = false; + } + + return $this; + } + + /** + * Remove all validators + * + * @return AbstractAdapter + */ + public function clearValidators() + { + $this->validators = array(); + foreach (array_keys($this->files) as $file) { + $this->files[$file]['validators'] = array(); + $this->files[$file]['validated'] = false; + } + + return $this; + } + + /** + * Sets Options for adapters + * + * @param array $options Options to set + * @param array $files (Optional) Files to set the options for + * @return AbstractAdapter + */ + public function setOptions($options = array(), $files = null) + { + $file = $this->getFiles($files, false, true); + + if (is_array($options)) { + if (empty($file)) { + $this->options = array_merge($this->options, $options); + } + + foreach ($options as $name => $value) { + foreach ($file as $key => $content) { + switch ($name) { + case 'magicFile' : + $this->files[$key]['options'][$name] = (string) $value; + break; + + case 'ignoreNoFile' : + case 'useByteString' : + case 'detectInfos' : + $this->files[$key]['options'][$name] = (bool) $value; + break; + + default: + continue; + } + } + } + } + + return $this; + } + + /** + * Returns set options for adapters or files + * + * @param array $files (Optional) Files to return the options for + * @return array Options for given files + */ + public function getOptions($files = null) + { + $file = $this->getFiles($files, false, true); + + foreach ($file as $key => $content) { + if (isset($this->files[$key]['options'])) { + $options[$key] = $this->files[$key]['options']; + } else { + $options[$key] = array(); + } + } + + return $options; + } + + /** + * Checks if the files are valid + * + * @param string|array $files (Optional) Files to check + * @return bool True if all checks are valid + */ + public function isValid($files = null) + { + $check = $this->getFiles($files, false, true); + if (empty($check)) { + return false; + } + + $translator = $this->getTranslator(); + $this->messages = array(); + $break = false; + foreach ($check as $content) { + if (array_key_exists('validators', $content) && + in_array('Zend\Validator\File\Count', $content['validators'])) { + $validator = $this->validators['Zend\Validator\File\Count']; + $count = $content; + if (empty($content['tmp_name'])) { + continue; + } + + if (array_key_exists('destination', $content)) { + $checkit = $content['destination']; + } else { + $checkit = dirname($content['tmp_name']); + } + + $checkit .= DIRECTORY_SEPARATOR . $content['name']; + $validator->addFile($checkit); + } + } + + if (isset($count)) { + if (!$validator->isValid($count['tmp_name'], $count)) { + $this->messages += $validator->getMessages(); + } + } + + foreach ($check as $key => $content) { + $fileerrors = array(); + if (array_key_exists('validators', $content) && $content['validated']) { + continue; + } + + if (array_key_exists('validators', $content)) { + foreach ($content['validators'] as $class) { + $validator = $this->validators[$class]; + if (method_exists($validator, 'setTranslator')) { + $validator->setTranslator($translator); + } + + if (($class === 'Zend\Validator\File\Upload') && (empty($content['tmp_name']))) { + $tocheck = $key; + } else { + $tocheck = $content['tmp_name']; + } + + if (!$validator->isValid($tocheck, $content)) { + $fileerrors += $validator->getMessages(); + } + + if (!empty($content['options']['ignoreNoFile']) && (isset($fileerrors['fileUploadErrorNoFile']))) { + unset($fileerrors['fileUploadErrorNoFile']); + break; + } + + if (($class === 'Zend\Validator\File\Upload') && (count($fileerrors) > 0)) { + break; + } + + if (($this->break[$class]) && (count($fileerrors) > 0)) { + $break = true; + break; + } + } + } + + if (count($fileerrors) > 0) { + $this->files[$key]['validated'] = false; + } else { + $this->files[$key]['validated'] = true; + } + + $this->messages += $fileerrors; + if ($break) { + break; + } + } + + if (count($this->messages) > 0) { + return false; + } + + return true; + } + + /** + * Returns found validation messages + * + * @return array + */ + public function getMessages() + { + return $this->messages; + } + + /** + * Retrieve error codes + * + * @return array + */ + public function getErrors() + { + return array_keys($this->messages); + } + + /** + * Are there errors registered? + * + * @return bool + */ + public function hasErrors() + { + return (!empty($this->messages)); + } + + /** + * Adds a new filter for this class + * + * @param string|Filter\FilterInterface $filter Type of filter to add + * @param string|array $options Options to set for the filter + * @param string|array $files Files to limit this filter to + * @return AbstractAdapter + * @throws Exception\InvalidArgumentException for invalid type + */ + public function addFilter($filter, $options = null, $files = null) + { + if (is_string($filter)) { + $filter = $this->getFilterManager()->get($filter, $options); + } + + if (!$filter instanceof Filter\FilterInterface) { + throw new Exception\InvalidArgumentException('Invalid filter specified'); + } + + $class = get_class($filter); + $this->filters[$class] = $filter; + $files = $this->getFiles($files, true, true); + foreach ($files as $file) { + $this->files[$file]['filters'][] = $class; + } + + return $this; + } + + /** + * Add Multiple filters at once + * + * @param array $filters + * @param string|array $files + * @return AbstractAdapter + */ + public function addFilters(array $filters, $files = null) + { + foreach ($filters as $key => $spec) { + if ($spec instanceof Filter\FilterInterface) { + $this->addFilter($spec, null, $files); + continue; + } + + if (is_string($key)) { + $this->addFilter($key, $spec, $files); + continue; + } + + if (is_int($key)) { + if (is_string($spec)) { + $this->addFilter($spec, null, $files); + continue; + } + + if (is_array($spec)) { + if (!array_key_exists('filter', $spec)) { + continue; + } + + $filter = $spec['filter']; + unset($spec['filter']); + $this->addFilter($filter, $spec, $files); + continue; + } + + continue; + } + } + + return $this; + } + + /** + * Sets a filter for the class, erasing all previous set + * + * @param array $filters Filter to set + * @param string|array $files Files to limit this filter to + * @return Filter\AbstractFilter + */ + public function setFilters(array $filters, $files = null) + { + $this->clearFilters(); + return $this->addFilters($filters, $files); + } + + /** + * Determine if a given filter has already been registered + * + * @param string $name + * @return bool + */ + public function hasFilter($name) + { + return (false !== $this->getFilterIdentifier($name)); + } + + /** + * Retrieve individual filter + * + * @param string $name + * @return Filter\FilterInterface|null + */ + public function getFilter($name) + { + if (false === ($identifier = $this->getFilterIdentifier($name))) { + return null; + } + + return $this->filters[$identifier]; + } + + /** + * Returns all set filters + * + * @param string|array $files (Optional) Returns the filter for this files + * @return array List of set filters + * @throws Exception\RuntimeException When file not found + */ + public function getFilters($files = null) + { + if ($files === null) { + return $this->filters; + } + + $files = $this->getFiles($files, true, true); + $filters = array(); + foreach ($files as $file) { + if (!empty($this->files[$file]['filters'])) { + $filters += $this->files[$file]['filters']; + } + } + + $filters = array_unique($filters); + $result = array(); + foreach ($filters as $filter) { + $result[] = $this->filters[$filter]; + } + + return $result; + } + + /** + * Remove an individual filter + * + * @param string $name + * @return AbstractAdapter + */ + public function removeFilter($name) + { + if (false === ($key = $this->getFilterIdentifier($name))) { + return $this; + } + + unset($this->filters[$key]); + foreach (array_keys($this->files) as $file) { + if (empty($this->files[$file]['filters'])) { + continue; + } + + $index = array_search($key, $this->files[$file]['filters']); + if ($index === false) { + continue; + } + + unset($this->files[$file]['filters'][$index]); + } + return $this; + } + + /** + * Remove all filters + * + * @return AbstractAdapter + */ + public function clearFilters() + { + $this->filters = array(); + foreach (array_keys($this->files) as $file) { + $this->files[$file]['filters'] = array(); + } + + return $this; + } + + /** + * Retrieves the filename of transferred files. + * + * @param string $file (Optional) Element to return the filename for + * @param bool $path (Optional) Should the path also be returned ? + * @return string|array + */ + public function getFileName($file = null, $path = true) + { + $files = $this->getFiles($file, true, true); + $result = array(); + $directory = ""; + foreach ($files as $file) { + if (empty($this->files[$file]['name'])) { + continue; + } + + if ($path === true) { + $directory = $this->getDestination($file) . DIRECTORY_SEPARATOR; + } + + $result[$file] = $directory . $this->files[$file]['name']; + } + + if (count($result) == 1) { + return current($result); + } + + return $result; + } + + /** + * Retrieve additional internal file informations for files + * + * @param string $file (Optional) File to get informations for + * @return array + */ + public function getFileInfo($file = null) + { + return $this->getFiles($file); + } + + /** + * Sets a new destination for the given files + * + * @deprecated Will be changed to be a filter!!! + * @param string $destination New destination directory + * @param string|array $files Files to set the new destination for + * @return AbstractAdapter + * @throws Exception\InvalidArgumentException when the given destination is not a directory or does not exist + */ + public function setDestination($destination, $files = null) + { + $orig = $files; + $destination = rtrim($destination, "/\\"); + if (!is_dir($destination)) { + throw new Exception\InvalidArgumentException('The given destination is not a directory or does not exist'); + } + + if (!is_writable($destination)) { + throw new Exception\InvalidArgumentException('The given destination is not writeable'); + } + + if ($files === null) { + foreach ($this->files as $file => $content) { + $this->files[$file]['destination'] = $destination; + } + } else { + $files = $this->getFiles($files, true, true); + if (empty($files) and is_string($orig)) { + $this->files[$orig]['destination'] = $destination; + } + + foreach ($files as $file) { + $this->files[$file]['destination'] = $destination; + } + } + + return $this; + } + + /** + * Retrieve destination directory value + * + * @param null|string|array $files + * @throws Exception\InvalidArgumentException + * @return null|string|array + */ + public function getDestination($files = null) + { + $orig = $files; + $files = $this->getFiles($files, false, true); + $destinations = array(); + if (empty($files) and is_string($orig)) { + if (isset($this->files[$orig]['destination'])) { + $destinations[$orig] = $this->files[$orig]['destination']; + } else { + throw new Exception\InvalidArgumentException( + sprintf('The file transfer adapter can not find "%s"', $orig) + ); + } + } + + foreach ($files as $key => $content) { + if (isset($this->files[$key]['destination'])) { + $destinations[$key] = $this->files[$key]['destination']; + } else { + $tmpdir = $this->getTmpDir(); + $this->setDestination($tmpdir, $key); + $destinations[$key] = $tmpdir; + } + } + + if (empty($destinations)) { + $destinations = $this->getTmpDir(); + } elseif (count($destinations) == 1) { + $destinations = current($destinations); + } + + return $destinations; + } + + /** + * Sets translator to use in helper + * + * @param Translator $translator [optional] translator. + * Default is null, which sets no translator. + * @param string $textDomain [optional] text domain + * Default is null, which skips setTranslatorTextDomain + * @return AbstractAdapter + */ + public function setTranslator(Translator $translator = null, $textDomain = null) + { + $this->translator = $translator; + if (null !== $textDomain) { + $this->setTranslatorTextDomain($textDomain); + } + return $this; + } + + /** + * Retrieve localization translator object + * + * @return Translator|null + */ + public function getTranslator() + { + if ($this->isTranslatorEnabled()) { + return null; + } + + return $this->translator; + } + + /** + * Checks if the helper has a translator + * + * @return bool + */ + public function hasTranslator() + { + return (bool) $this->getTranslator(); + } + + /** + * Indicate whether or not translation should be enabled + * + * @param bool $flag + * @return AbstractAdapter + */ + public function setTranslatorEnabled($flag = true) + { + $this->translatorEnabled = (bool) $flag; + return $this; + } + + /** + * Is translation enabled? + * + * @return bool + */ + public function isTranslatorEnabled() + { + return $this->translatorEnabled; + } + + /** + * Set translation text domain + * + * @param string $textDomain + * @return AbstractAdapter + */ + public function setTranslatorTextDomain($textDomain = 'default') + { + $this->translatorTextDomain = $textDomain; + return $this; + } + + /** + * Return the translation text domain + * + * @return string + */ + public function getTranslatorTextDomain() + { + return $this->translatorTextDomain; + } + + /** + * Returns the hash for a given file + * + * @param string $hash Hash algorithm to use + * @param string|array $files Files to return the hash for + * @return string|array Hashstring + * @throws Exception\InvalidArgumentException On unknown hash algorithm + */ + public function getHash($hash = 'crc32', $files = null) + { + if (!in_array($hash, hash_algos())) { + throw new Exception\InvalidArgumentException('Unknown hash algorithm'); + } + + $files = $this->getFiles($files); + $result = array(); + foreach ($files as $key => $value) { + if (file_exists($value['name'])) { + $result[$key] = hash_file($hash, $value['name']); + } elseif (file_exists($value['tmp_name'])) { + $result[$key] = hash_file($hash, $value['tmp_name']); + } elseif (empty($value['options']['ignoreNoFile'])) { + throw new Exception\InvalidArgumentException("The file '{$value['name']}' does not exist"); + } + } + + if (count($result) == 1) { + return current($result); + } + + return $result; + } + + /** + * Returns the real filesize of the file + * + * @param string|array $files Files to get the filesize from + * @return string|array Filesize + * @throws Exception\InvalidArgumentException When the file does not exist + */ + public function getFileSize($files = null) + { + $files = $this->getFiles($files); + $result = array(); + foreach ($files as $key => $value) { + if (file_exists($value['name']) || file_exists($value['tmp_name'])) { + if ($value['options']['useByteString']) { + $result[$key] = static::toByteString($value['size']); + } else { + $result[$key] = $value['size']; + } + } elseif (empty($value['options']['ignoreNoFile'])) { + throw new Exception\InvalidArgumentException("The file '{$value['name']}' does not exist"); + } else { + continue; + } + } + + if (count($result) == 1) { + return current($result); + } + + return $result; + } + + /** + * Internal method to detect the size of a file + * + * @param array $value File infos + * @return string Filesize of given file + */ + protected function detectFileSize($value) + { + if (file_exists($value['name'])) { + $filename = $value['name']; + } elseif (file_exists($value['tmp_name'])) { + $filename = $value['tmp_name']; + } else { + return null; + } + + ErrorHandler::start(); + $filesize = filesize($filename); + $return = ErrorHandler::stop(); + if ($return instanceof ErrorException) { + $filesize = 0; + } + + return sprintf("%u", $filesize); + } + + /** + * Returns the real mimetype of the file + * Uses fileinfo, when not available mime_magic and as last fallback a manual given mimetype + * + * @param string|array $files Files to get the mimetype from + * @return string|array MimeType + * @throws Exception\InvalidArgumentException When the file does not exist + */ + public function getMimeType($files = null) + { + $files = $this->getFiles($files); + $result = array(); + foreach ($files as $key => $value) { + if (file_exists($value['name']) || file_exists($value['tmp_name'])) { + $result[$key] = $value['type']; + } elseif (empty($value['options']['ignoreNoFile'])) { + throw new Exception\InvalidArgumentException("the file '{$value['name']}' does not exist"); + } else { + continue; + } + } + + if (count($result) == 1) { + return current($result); + } + + return $result; + } + + /** + * Internal method to detect the mime type of a file + * + * @param array $value File infos + * @return string Mimetype of given file + */ + protected function detectMimeType($value) + { + if (file_exists($value['name'])) { + $file = $value['name']; + } elseif (file_exists($value['tmp_name'])) { + $file = $value['tmp_name']; + } else { + return null; + } + + if (class_exists('finfo', false)) { + $const = defined('FILEINFO_MIME_TYPE') ? FILEINFO_MIME_TYPE : FILEINFO_MIME; + if (!empty($value['options']['magicFile'])) { + ErrorHandler::start(); + $mime = finfo_open($const, $value['options']['magicFile']); + ErrorHandler::stop(); + } + + if (empty($mime)) { + ErrorHandler::start(); + $mime = finfo_open($const); + ErrorHandler::stop(); + } + + if (!empty($mime)) { + $result = finfo_file($mime, $file); + } + + unset($mime); + } + + if (empty($result) && (function_exists('mime_content_type') + && ini_get('mime_magic.magicfile'))) { + $result = mime_content_type($file); + } + + if (empty($result)) { + $result = 'application/octet-stream'; + } + + return $result; + } + + /** + * Returns the formatted size + * + * @param integer $size + * @return string + */ + protected static function toByteString($size) + { + $sizes = array('B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'); + for ($i=0; $size >= 1024 && $i < 9; $i++) { + $size /= 1024; + } + + return round($size, 2) . $sizes[$i]; + } + + /** + * Internal function to filter all given files + * + * @param string|array $files (Optional) Files to check + * @return bool False on error + */ + protected function filter($files = null) + { + $check = $this->getFiles($files); + foreach ($check as $name => $content) { + if (array_key_exists('filters', $content)) { + foreach ($content['filters'] as $class) { + $filter = $this->filters[$class]; + try { + $result = $filter->filter($this->getFileName($name)); + + $this->files[$name]['destination'] = dirname($result); + $this->files[$name]['name'] = basename($result); + } catch (FilterException\ExceptionInterface $e) { + $this->messages += array($e->getMessage()); + } + } + } + } + + if (count($this->messages) > 0) { + return false; + } + + return true; + } + + /** + * Determine system TMP directory and detect if we have read access + * + * @return string + * @throws Exception\RuntimeException if unable to determine directory + */ + protected function getTmpDir() + { + if (null === $this->tmpDir) { + $tmpdir = array(); + if (function_exists('sys_get_temp_dir')) { + $tmpdir[] = sys_get_temp_dir(); + } + + if (!empty($_ENV['TMP'])) { + $tmpdir[] = realpath($_ENV['TMP']); + } + + if (!empty($_ENV['TMPDIR'])) { + $tmpdir[] = realpath($_ENV['TMPDIR']); + } + + if (!empty($_ENV['TEMP'])) { + $tmpdir[] = realpath($_ENV['TEMP']); + } + + $upload = ini_get('upload_tmp_dir'); + if ($upload) { + $tmpdir[] = realpath($upload); + } + + foreach ($tmpdir as $directory) { + if ($this->isPathWriteable($directory)) { + $this->tmpDir = $directory; + } + } + + if (empty($this->tmpDir)) { + // Attemp to detect by creating a temporary file + $tempFile = tempnam(md5(uniqid(rand(), true)), ''); + if ($tempFile) { + $this->tmpDir = realpath(dirname($tempFile)); + unlink($tempFile); + } else { + throw new Exception\RuntimeException('Could not determine a temporary directory'); + } + } + + $this->tmpDir = rtrim($this->tmpDir, "/\\"); + } + return $this->tmpDir; + } + + /** + * Tries to detect if we can read and write to the given path + * + * @param string $path + * @return bool + */ + protected function isPathWriteable($path) + { + $tempFile = rtrim($path, "/\\"); + $tempFile .= '/' . 'test.1'; + + ErrorHandler::start(); + $result = file_put_contents($tempFile, 'TEST'); + ErrorHandler::stop(); + + if ($result == false) { + return false; + } + + ErrorHandler::start(); + $result = unlink($tempFile); + ErrorHandler::stop(); + + if ($result == false) { + return false; + } + + return true; + } + + /** + * Returns found files based on internal file array and given files + * + * @param string|array $files (Optional) Files to return + * @param bool $names (Optional) Returns only names on true, else complete info + * @param bool $noexception (Optional) Allows throwing an exception, otherwise returns an empty array + * @return array Found files + * @throws Exception\RuntimeException On false filename + */ + protected function getFiles($files, $names = false, $noexception = false) + { + $check = array(); + + if (is_string($files)) { + $files = array($files); + } + + if (is_array($files)) { + foreach ($files as $find) { + $found = array(); + foreach ($this->files as $file => $content) { + if (!isset($content['name'])) { + continue; + } + + if (($content['name'] === $find) && isset($content['multifiles'])) { + foreach ($content['multifiles'] as $multifile) { + $found[] = $multifile; + } + break; + } + + if ($file === $find) { + $found[] = $file; + break; + } + + if ($content['name'] === $find) { + $found[] = $file; + break; + } + } + + if (empty($found)) { + if ($noexception !== false) { + return array(); + } + + throw new Exception\RuntimeException(sprintf('The file transfer adapter can not find "%s"', $find)); + } + + foreach ($found as $checked) { + $check[$checked] = $this->files[$checked]; + } + } + } + + if ($files === null) { + $check = $this->files; + $keys = array_keys($check); + foreach ($keys as $key) { + if (isset($check[$key]['multifiles'])) { + unset($check[$key]); + } + } + } + + if ($names) { + $check = array_keys($check); + } + + return $check; + } + + /** + * Retrieve internal identifier for a named validator + * + * @param string $name + * @return string + */ + protected function getValidatorIdentifier($name) + { + if (array_key_exists($name, $this->validators)) { + return $name; + } + + foreach (array_keys($this->validators) as $test) { + if (preg_match('/' . preg_quote($name) . '$/i', $test)) { + return $test; + } + } + + return false; + } + + /** + * Retrieve internal identifier for a named filter + * + * @param string $name + * @return string + */ + protected function getFilterIdentifier($name) + { + if (array_key_exists($name, $this->filters)) { + return $name; + } + + foreach (array_keys($this->filters) as $test) { + if (preg_match('/' . preg_quote($name) . '$/i', $test)) { + return $test; + } + } + + return false; + } +} diff --git a/src/Transfer/Adapter/FilterPluginManager.php b/src/Transfer/Adapter/FilterPluginManager.php new file mode 100644 index 0000000..91e0a57 --- /dev/null +++ b/src/Transfer/Adapter/FilterPluginManager.php @@ -0,0 +1,38 @@ +'filedecrypt', + 'encrypt' =>'fileencrypt', + 'lowercase' =>'filelowercase', + 'rename' =>'filerename', + 'uppercase' =>'fileuppercase', + ); +} diff --git a/src/Transfer/Adapter/Http.php b/src/Transfer/Adapter/Http.php new file mode 100644 index 0000000..e6fa968 --- /dev/null +++ b/src/Transfer/Adapter/Http.php @@ -0,0 +1,468 @@ +setOptions($options); + $this->prepareFiles(); + $this->addValidator('Upload', false, $this->files); + } + + /** + * Sets a validator for the class, erasing all previous set + * + * @param array $validators Validator to set + * @param string|array $files Files to limit this validator to + * @return AbstractAdapter + */ + public function setValidators(array $validators, $files = null) + { + $this->clearValidators(); + return $this->addValidators($validators, $files); + } + + /** + * Remove an individual validator + * + * @param string $name + * @return AbstractAdapter + */ + public function removeValidator($name) + { + if ($name == 'Upload') { + return $this; + } + + return parent::removeValidator($name); + } + + /** + * Clear the validators + * + * @return AbstractAdapter + */ + public function clearValidators() + { + parent::clearValidators(); + $this->addValidator('Upload', false, $this->files); + + return $this; + } + + /** + * Send the file to the client (Download) + * + * @param string|array $options Options for the file(s) to send + * @return void + * @throws Exception\BadMethodCallException Not implemented + */ + public function send($options = null) + { + throw new Exception\BadMethodCallException('Method not implemented'); + } + + /** + * Checks if the files are valid + * + * @param string|array $files (Optional) Files to check + * @return bool True if all checks are valid + */ + public function isValid($files = null) + { + // Workaround for WebServer not conforming HTTP and omitting CONTENT_LENGTH + $content = 0; + if (isset($_SERVER['CONTENT_LENGTH'])) { + $content = $_SERVER['CONTENT_LENGTH']; + } elseif (!empty($_POST)) { + $content = serialize($_POST); + } + + // Workaround for a PHP error returning empty $_FILES when form data exceeds php settings + if (empty($this->files) && ($content > 0)) { + if (is_array($files)) { + $files = current($files); + } + + $temp = array($files => array( + 'name' => $files, + 'error' => 1)); + $validator = $this->validators['Zend\Validator\File\Upload']; + $validator->setTranslator($this->getTranslator()) + ->setFiles($temp) + ->isValid($files, null); + $this->messages += $validator->getMessages(); + return false; + } + + return parent::isValid($files); + } + + /** + * Receive the file from the client (Upload) + * + * @param string|array $files (Optional) Files to receive + * @return bool + */ + public function receive($files = null) + { + if (!$this->isValid($files)) { + return false; + } + + $check = $this->getFiles($files); + foreach ($check as $file => $content) { + if (!$content['received']) { + $directory = ''; + $destination = $this->getDestination($file); + if ($destination !== null) { + $directory = $destination . DIRECTORY_SEPARATOR; + } + + $filename = $directory . $content['name']; + $rename = $this->getFilter('Rename'); + if ($rename !== null) { + $tmp = $rename->getNewName($content['tmp_name']); + if ($tmp != $content['tmp_name']) { + $filename = $tmp; + } + + if (dirname($filename) == '.') { + $filename = $directory . $filename; + } + + $key = array_search(get_class($rename), $this->files[$file]['filters']); + unset($this->files[$file]['filters'][$key]); + } + + // Should never return false when it's tested by the upload validator + if (!move_uploaded_file($content['tmp_name'], $filename)) { + if ($content['options']['ignoreNoFile']) { + $this->files[$file]['received'] = true; + $this->files[$file]['filtered'] = true; + continue; + } + + $this->files[$file]['received'] = false; + return false; + } + + if ($rename !== null) { + $this->files[$file]['destination'] = dirname($filename); + $this->files[$file]['name'] = basename($filename); + } + + $this->files[$file]['tmp_name'] = $filename; + $this->files[$file]['received'] = true; + } + + if (!$content['filtered']) { + if (!$this->filter($file)) { + $this->files[$file]['filtered'] = false; + return false; + } + + $this->files[$file]['filtered'] = true; + } + } + + return true; + } + + /** + * Checks if the file was already sent + * + * @param string|array $files Files to check + * @return bool + * @throws Exception\BadMethodCallException Not implemented + */ + public function isSent($files = null) + { + throw new Exception\BadMethodCallException('Method not implemented'); + } + + /** + * Checks if the file was already received + * + * @param string|array $files (Optional) Files to check + * @return bool + */ + public function isReceived($files = null) + { + $files = $this->getFiles($files, false, true); + if (empty($files)) { + return false; + } + + foreach ($files as $content) { + if ($content['received'] !== true) { + return false; + } + } + + return true; + } + + /** + * Checks if the file was already filtered + * + * @param string|array $files (Optional) Files to check + * @return bool + */ + public function isFiltered($files = null) + { + $files = $this->getFiles($files, false, true); + if (empty($files)) { + return false; + } + + foreach ($files as $content) { + if ($content['filtered'] !== true) { + return false; + } + } + + return true; + } + + /** + * Has a file been uploaded ? + * + * @param array|string|null $files + * @return bool + */ + public function isUploaded($files = null) + { + $files = $this->getFiles($files, false, true); + if (empty($files)) { + return false; + } + + foreach ($files as $file) { + if (empty($file['name'])) { + return false; + } + } + + return true; + } + + /** + * Returns the actual progress of file up-/downloads + * + * @param string|array $id The upload to get the progress for + * @return array|null + * @throws Exception\PhpEnvironmentException whether APC nor UploadProgress extension installed + * @throws Exception\RuntimeException + */ + public static function getProgress($id = null) + { + if (!static::isApcAvailable() && !static::isUploadProgressAvailable()) { + throw new Exception\PhpEnvironmentException('Neither APC nor UploadProgress extension installed'); + } + + $session = 'Zend\File\Transfer\Adapter\Http\ProgressBar'; + $status = array( + 'total' => 0, + 'current' => 0, + 'rate' => 0, + 'message' => '', + 'done' => false + ); + + if (is_array($id)) { + if (isset($id['progress'])) { + $adapter = $id['progress']; + } + + if (isset($id['session'])) { + $session = $id['session']; + } + + if (isset($id['id'])) { + $id = $id['id']; + } else { + unset($id); + } + } + + if (!empty($id) && (($id instanceof Adapter\AbstractAdapter) || ($id instanceof ProgressBar\ProgressBar))) { + $adapter = $id; + unset($id); + } + + if (empty($id)) { + if (!isset($_GET['progress_key'])) { + $status['message'] = 'No upload in progress'; + $status['done'] = true; + } else { + $id = $_GET['progress_key']; + } + } + + if (!empty($id)) { + if (static::isApcAvailable()) { + + $call = call_user_func(static::$callbackApc, ini_get('apc.rfc1867_prefix') . $id); + if (is_array($call)) { + $status = $call + $status; + } + } elseif (static::isUploadProgressAvailable()) { + $call = call_user_func(static::$callbackUploadProgress, $id); + if (is_array($call)) { + $status = $call + $status; + $status['total'] = $status['bytes_total']; + $status['current'] = $status['bytes_uploaded']; + $status['rate'] = $status['speed_average']; + if ($status['total'] == $status['current']) { + $status['done'] = true; + } + } + } + + if (!is_array($call)) { + $status['done'] = true; + $status['message'] = 'Failure while retrieving the upload progress'; + } elseif (!empty($status['cancel_upload'])) { + $status['done'] = true; + $status['message'] = 'The upload has been canceled'; + } else { + $status['message'] = static::toByteString($status['current']) . " - " . static::toByteString($status['total']); + } + + $status['id'] = $id; + } + + if (isset($adapter) && isset($status['id'])) { + if ($adapter instanceof Adapter\AbstractAdapter) { + $adapter = new ProgressBar\ProgressBar($adapter, 0, $status['total'], $session); + } + + if (!($adapter instanceof ProgressBar\ProgressBar)) { + throw new Exception\RuntimeException('Unknown Adapter given'); + } + + if ($status['done']) { + $adapter->finish(); + } else { + $adapter->update($status['current'], $status['message']); + } + + $status['progress'] = $adapter; + } + + return $status; + } + + /** + * Checks the APC extension for progress information + * + * @return bool + */ + public static function isApcAvailable() + { + return (bool) ini_get('apc.enabled') && (bool) ini_get('apc.rfc1867') && is_callable(static::$callbackApc); + } + + /** + * Checks the UploadProgress extension for progress information + * + * @return bool + */ + public static function isUploadProgressAvailable() + { + return is_callable(static::$callbackUploadProgress); + } + + /** + * Prepare the $_FILES array to match the internal syntax of one file per entry + * + * @return Http + */ + protected function prepareFiles() + { + $this->files = array(); + foreach ($_FILES as $form => $content) { + if (is_array($content['name'])) { + foreach ($content as $param => $file) { + foreach ($file as $number => $target) { + $this->files[$form . '_' . $number . '_'][$param] = $target; + $this->files[$form]['multifiles'][$number] = $form . '_' . $number . '_'; + } + } + + $this->files[$form]['name'] = $form; + foreach ($this->files[$form]['multifiles'] as $key => $value) { + $this->files[$value]['options'] = $this->options; + $this->files[$value]['validated'] = false; + $this->files[$value]['received'] = false; + $this->files[$value]['filtered'] = false; + + $mimetype = $this->detectMimeType($this->files[$value]); + $this->files[$value]['type'] = $mimetype; + + $filesize = $this->detectFileSize($this->files[$value]); + $this->files[$value]['size'] = $filesize; + + if ($this->options['detectInfos']) { + $_FILES[$form]['type'][$key] = $mimetype; + $_FILES[$form]['size'][$key] = $filesize; + } + } + } else { + $this->files[$form] = $content; + $this->files[$form]['options'] = $this->options; + $this->files[$form]['validated'] = false; + $this->files[$form]['received'] = false; + $this->files[$form]['filtered'] = false; + + $mimetype = $this->detectMimeType($this->files[$form]); + $this->files[$form]['type'] = $mimetype; + + $filesize = $this->detectFileSize($this->files[$form]); + $this->files[$form]['size'] = $filesize; + + if ($this->options['detectInfos']) { + $_FILES[$form]['type'] = $mimetype; + $_FILES[$form]['size'] = $filesize; + } + } + } + + return $this; + } +} diff --git a/src/Transfer/Adapter/ValidatorPluginManager.php b/src/Transfer/Adapter/ValidatorPluginManager.php new file mode 100644 index 0000000..ff68e5a --- /dev/null +++ b/src/Transfer/Adapter/ValidatorPluginManager.php @@ -0,0 +1,41 @@ +'filecount', + 'crc32' =>'filecrc32', + 'excludeextension' =>'fileexcludeextension', + 'excludemimetype' =>'fileexcludemimetype', + 'exists' =>'fileexists', + 'extension' =>'fileextension', + 'filessize' =>'filefilessize', + 'hash' =>'filehash', + 'imagesize' =>'fileimagesize', + 'iscompressed' =>'fileiscompressed', + 'isimage' =>'fileisimage', + 'md5' =>'filemd5', + 'mimetype' =>'filemimetype', + 'notexists' =>'filenotexists', + 'sha1' =>'filesha1', + 'size' =>'filesize', + 'upload' =>'fileupload', + 'wordcount' =>'filewordcount', + ); +} diff --git a/src/Transfer/Exception/BadMethodCallException.php b/src/Transfer/Exception/BadMethodCallException.php new file mode 100644 index 0000000..c5e7a06 --- /dev/null +++ b/src/Transfer/Exception/BadMethodCallException.php @@ -0,0 +1,21 @@ +setAdapter($adapter, $direction, $options); + } + + /** + * Sets a new adapter + * + * @param string $adapter Adapter to use + * @param bool $direction OPTIONAL False means Download, true means upload + * @param array $options OPTIONAL Options to set for this adapter + * @return Transfer + * @throws Exception\InvalidArgumentException + */ + public function setAdapter($adapter, $direction = false, $options = array()) + { + if (!is_string($adapter)) { + throw new Exception\InvalidArgumentException('Adapter must be a string'); + } + + if ($adapter[0] != '\\') { + $adapter = '\Zend\File\Transfer\Adapter\\' . ucfirst($adapter); + } + + $direction = (integer) $direction; + $this->adapter[$direction] = new $adapter($options); + if (!$this->adapter[$direction] instanceof Adapter\AbstractAdapter) { + throw new Exception\InvalidArgumentException( + 'Adapter ' . $adapter . ' does not extend Zend\File\Transfer\Adapter\AbstractAdapter' + ); + } + + return $this; + } + + /** + * Returns all set adapters + * + * @param bool $direction On null, all directions are returned + * On false, download direction is returned + * On true, upload direction is returned + * @return array|Adapter\AbstractAdapter + */ + public function getAdapter($direction = null) + { + if ($direction === null) { + return $this->adapter; + } + + $direction = (integer) $direction; + return $this->adapter[$direction]; + } + + /** + * Calls all methods from the adapter + * + * @param string $method Method to call + * @param array $options Options for this method + * @throws Exception\BadMethodCallException if unknown method + * @return mixed + */ + public function __call($method, array $options) + { + if (array_key_exists('direction', $options)) { + $direction = (integer) $options['direction']; + } else { + $direction = 0; + } + + if (method_exists($this->adapter[$direction], $method)) { + return call_user_func_array(array($this->adapter[$direction], $method), $options); + } + + throw new Exception\BadMethodCallException("Unknown method '" . $method . "' called!"); + } +} diff --git a/test/Transfer/Adapter/AbstractAdapterTestMockAdapter.php b/test/Transfer/Adapter/AbstractAdapterTestMockAdapter.php new file mode 100644 index 0000000..fa5fde6 --- /dev/null +++ b/test/Transfer/Adapter/AbstractAdapterTestMockAdapter.php @@ -0,0 +1,152 @@ +files = array( + 'foo' => array( + 'name' => 'foo.jpg', + 'type' => 'image/jpeg', + 'size' => 126976, + 'tmp_name' => '/tmp/489127ba5c89c', + 'options' => array('ignoreNoFile' => false, 'useByteString' => true, 'detectInfos' => true), + 'validated' => false, + 'received' => false, + 'filtered' => false, + ), + 'bar' => array( + 'name' => 'bar.png', + 'type' => 'image/png', + 'size' => 91136, + 'tmp_name' => '/tmp/489128284b51f', + 'options' => array('ignoreNoFile' => false, 'useByteString' => true), + 'validated' => false, + 'received' => false, + 'filtered' => false, + ), + 'baz' => array( + 'name' => 'baz.text', + 'type' => 'text/plain', + 'size' => 1172, + 'tmp_name' => $testfile, + 'options' => array('ignoreNoFile' => false, 'useByteString' => true), + 'validated' => false, + 'received' => false, + 'filtered' => false, + ), + 'file_0_' => array( + 'name' => 'foo.jpg', + 'type' => 'image/jpeg', + 'size' => 126976, + 'tmp_name' => '/tmp/489127ba5c89c', + 'options' => array('ignoreNoFile' => false, 'useByteString' => true), + 'validated' => false, + 'received' => false, + 'filtered' => false, + ), + 'file_1_' => array( + 'name' => 'baz.text', + 'type' => 'text/plain', + 'size' => 1172, + 'tmp_name' => $testfile, + 'options' => array('ignoreNoFile' => false, 'useByteString' => true), + 'validated' => false, + 'received' => false, + 'filtered' => false, + ), + 'file' => array( + 'name' => 'foo.jpg', + 'multifiles' => array(0 => 'file_0_', 1 => 'file_1_') + ), + ); + } + + public function send($options = null) + { + return; + } + + public function receive($options = null) + { + $this->received = true; + return; + } + + public function isSent($file = null) + { + return false; + } + + public function isReceived($file = null) + { + return $this->received; + } + + public function isUploaded($files = null) + { + return true; + } + + public function isFiltered($files = null) + { + return true; + } + + public static function getProgress() + { + return; + } + + public function getTmpDir() + { + $this->tmpDir = parent::getTmpDir(); + } + + public function isPathWriteable($path) + { + return parent::isPathWriteable($path); + } + + public function addInvalidFile() + { + $this->files += array( + 'test' => array( + 'name' => 'test.txt', + 'type' => 'image/jpeg', + 'size' => 0, + 'tmp_name' => '', + 'options' => array('ignoreNoFile' => true, 'useByteString' => true), + 'validated' => false, + 'received' => false, + 'filtered' => false, + ) + ); + } + +} diff --git a/test/Transfer/Adapter/AbstractTest.php b/test/Transfer/Adapter/AbstractTest.php new file mode 100644 index 0000000..a593e9a --- /dev/null +++ b/test/Transfer/Adapter/AbstractTest.php @@ -0,0 +1,670 @@ +adapter = new AbstractAdapterTestMockAdapter(); + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + * + * @return void + */ + public function tearDown() + { + } + + public function testAdapterShouldLazyLoadValidatorPluginManager() + { + $loader = $this->adapter->getValidatorManager(); + $this->assertInstanceOf('Zend\File\Transfer\Adapter\ValidatorPluginManager', $loader); + } + + public function testAdapterShouldAllowSettingFilterPluginManagerInstance() + { + $manager = new File\Transfer\Adapter\FilterPluginManager(); + $this->adapter->setFilterManager($manager); + $this->assertSame($manager, $this->adapter->getFilterManager()); + } + + public function testAdapterShouldAllowAddingValidatorInstance() + { + $validator = new FileValidator\Count(array('min' => 1, 'max' => 1)); + $this->adapter->addValidator($validator); + $test = $this->adapter->getValidator('Zend\Validator\File\Count'); + $this->assertSame($validator, $test); + } + + public function testAdapterShouldAllowAddingValidatorViaPluginManager() + { + $this->adapter->addValidator('Count', false, array('min' => 1, 'max' => 1)); + $test = $this->adapter->getValidator('Count'); + $this->assertTrue($test instanceof FileValidator\Count); + } + + public function testAdapterhShouldRaiseExceptionWhenAddingInvalidValidatorType() + { + $this->setExpectedException('Zend\File\Transfer\Exception\InvalidArgumentException', 'Invalid validator provided to addValidator'); + $this->adapter->addValidator(new Filter\BaseName); + } + + public function testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader() + { + $validators = array( + 'count' => array('min' => 1, 'max' => 1), + 'Exists' => 'C:\temp', + array( + 'validator' => 'Upload', + 'options' => array(realpath(__FILE__)) + ), + new FileValidator\Extension('jpg'), + ); + $this->adapter->addValidators($validators); + $test = $this->adapter->getValidators(); + $this->assertTrue(is_array($test)); + $this->assertEquals(4, count($test), var_export($test, 1)); + $count = array_shift($test); + $this->assertTrue($count instanceof FileValidator\Count); + $exists = array_shift($test); + $this->assertTrue($exists instanceof FileValidator\Exists); + $size = array_shift($test); + $this->assertTrue($size instanceof FileValidator\Upload); + $ext = array_shift($test); + $this->assertTrue($ext instanceof FileValidator\Extension); + $orig = array_pop($validators); + $this->assertSame($orig, $ext); + } + + public function testGetValidatorShouldReturnNullWhenNoMatchingIdentifierExists() + { + $this->assertNull($this->adapter->getValidator('Between')); + } + + public function testAdapterShouldAllowPullingValidatorsByFile() + { + $this->adapter->addValidator('Between', false, false, 'foo'); + $validators = $this->adapter->getValidators('foo'); + $this->assertEquals(1, count($validators)); + $validator = array_shift($validators); + $this->assertTrue($validator instanceof Validator\Between); + } + + public function testCallingSetValidatorsOnAdapterShouldOverwriteExistingValidators() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $validators = array( + new FileValidator\Count(1), + new FileValidator\Extension('jpg'), + ); + $this->adapter->setValidators($validators); + $test = $this->adapter->getValidators(); + $this->assertSame($validators, array_values($test)); + } + + public function testAdapterShouldAllowRetrievingValidatorInstancesByClassName() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $ext = $this->adapter->getValidator('Zend\Validator\File\Extension'); + $this->assertTrue($ext instanceof FileValidator\Extension); + } + + public function testAdapterShouldAllowRetrievingValidatorInstancesByPluginName() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $count = $this->adapter->getValidator('Count'); + $this->assertTrue($count instanceof FileValidator\Count); + } + + public function testAdapterShouldAllowRetrievingAllValidatorsAtOnce() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $validators = $this->adapter->getValidators(); + $this->assertTrue(is_array($validators)); + $this->assertEquals(4, count($validators)); + foreach ($validators as $validator) { + $this->assertTrue($validator instanceof Validator\ValidatorInterface); + } + } + + public function testAdapterShouldAllowRemovingValidatorInstancesByClassName() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $this->assertTrue($this->adapter->hasValidator('Zend\Validator\File\Extension')); + $this->adapter->removeValidator('Zend\Validator\File\Extension'); + $this->assertFalse($this->adapter->hasValidator('Zend\Validator\File\Extension')); + } + + public function testAdapterShouldAllowRemovingValidatorInstancesByPluginName() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $this->assertTrue($this->adapter->hasValidator('Count')); + $this->adapter->removeValidator('Count'); + $this->assertFalse($this->adapter->hasValidator('Count')); + } + + public function testRemovingNonexistentValidatorShouldDoNothing() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $validators = $this->adapter->getValidators(); + $this->assertFalse($this->adapter->hasValidator('Between')); + $this->adapter->removeValidator('Between'); + $this->assertFalse($this->adapter->hasValidator('Between')); + $test = $this->adapter->getValidators(); + $this->assertSame($validators, $test); + } + + public function testAdapterShouldAllowRemovingAllValidatorsAtOnce() + { + $this->testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoader(); + $this->adapter->clearValidators(); + $validators = $this->adapter->getValidators(); + $this->assertTrue(is_array($validators)); + $this->assertEquals(0, count($validators)); + } + + public function testValidationShouldReturnTrueForValidTransfer() + { + $this->adapter->addValidator('Count', false, array(1, 3), 'foo'); + $this->assertTrue($this->adapter->isValid('foo')); + } + + public function testValidationShouldReturnTrueForValidTransferOfMultipleFiles() + { + $this->assertTrue($this->adapter->isValid(null)); + } + + public function testValidationShouldReturnFalseForInvalidTransfer() + { + $this->adapter->addValidator('Extension', false, 'png', 'foo'); + $this->assertFalse($this->adapter->isValid('foo')); + } + + public function testValidationShouldThrowExceptionForNonexistentFile() + { + $this->assertFalse($this->adapter->isValid('bogus')); + } + + public function testErrorMessagesShouldBeEmptyByDefault() + { + $messages = $this->adapter->getMessages(); + $this->assertTrue(is_array($messages)); + $this->assertEquals(0, count($messages)); + } + + public function testErrorMessagesShouldBePopulatedAfterInvalidTransfer() + { + $this->testValidationShouldReturnFalseForInvalidTransfer(); + $messages = $this->adapter->getMessages(); + $this->assertTrue(is_array($messages)); + $this->assertFalse(empty($messages)); + } + + public function testErrorCodesShouldBeNullByDefault() + { + $errors = $this->adapter->getErrors(); + $this->assertTrue(is_array($errors)); + $this->assertEquals(0, count($errors)); + } + + public function testErrorCodesShouldBePopulatedAfterInvalidTransfer() + { + $this->testValidationShouldReturnFalseForInvalidTransfer(); + $errors = $this->adapter->getErrors(); + $this->assertTrue(is_array($errors)); + $this->assertFalse(empty($errors)); + } + + public function testAdapterShouldLazyLoadFilterPluginManager() + { + $loader = $this->adapter->getFilterManager(); + $this->assertInstanceOf('Zend\File\Transfer\Adapter\FilterPluginManager', $loader); + } + + public function testAdapterShouldAllowAddingFilterInstance() + { + $filter = new Filter\StringToLower(); + $this->adapter->addFilter($filter); + $test = $this->adapter->getFilter('Zend\Filter\StringToLower'); + $this->assertSame($filter, $test); + } + + public function testAdapterShouldAllowAddingFilterViaPluginManager() + { + $this->adapter->addFilter('StringTrim'); + $test = $this->adapter->getFilter('StringTrim'); + $this->assertTrue($test instanceof Filter\StringTrim); + } + + + public function testAdapterhShouldRaiseExceptionWhenAddingInvalidFilterType() + { + $this->setExpectedException('Zend\File\Transfer\Exception\InvalidArgumentException', 'Invalid filter specified'); + $this->adapter->addFilter(new FileValidator\Extension('jpg')); + } + + public function testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader() + { + $filters = array( + 'Word\SeparatorToCamelCase' => array('separator' => ' '), + array( + 'filter' => 'Boolean', + 'casting' => true + ), + new Filter\BaseName(), + ); + $this->adapter->addFilters($filters); + $test = $this->adapter->getFilters(); + $this->assertTrue(is_array($test)); + $this->assertEquals(3, count($test), var_export($test, 1)); + $count = array_shift($test); + $this->assertTrue($count instanceof Word\SeparatorToCamelCase); + $size = array_shift($test); + $this->assertTrue($size instanceof Filter\Boolean); + $ext = array_shift($test); + $orig = array_pop($filters); + $this->assertSame($orig, $ext); + } + + public function testGetFilterShouldReturnNullWhenNoMatchingIdentifierExists() + { + $this->assertNull($this->adapter->getFilter('Boolean')); + } + + public function testAdapterShouldAllowPullingFiltersByFile() + { + $this->adapter->addFilter('Boolean', 1, 'foo'); + $filters = $this->adapter->getFilters('foo'); + $this->assertEquals(1, count($filters)); + $filter = array_shift($filters); + $this->assertTrue($filter instanceof Filter\Boolean); + } + + public function testCallingSetFiltersOnAdapterShouldOverwriteExistingFilters() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $filters = array( + new Filter\StringToUpper(), + new Filter\Boolean(), + ); + $this->adapter->setFilters($filters); + $test = $this->adapter->getFilters(); + $this->assertSame($filters, array_values($test)); + } + + public function testAdapterShouldAllowRetrievingFilterInstancesByClassName() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $ext = $this->adapter->getFilter('Zend\Filter\BaseName'); + $this->assertTrue($ext instanceof Filter\BaseName); + } + + public function testAdapterShouldAllowRetrievingFilterInstancesByPluginName() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $count = $this->adapter->getFilter('Boolean'); + $this->assertTrue($count instanceof Filter\Boolean); + } + + public function testAdapterShouldAllowRetrievingAllFiltersAtOnce() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $filters = $this->adapter->getFilters(); + $this->assertTrue(is_array($filters)); + $this->assertEquals(3, count($filters)); + foreach ($filters as $filter) { + $this->assertTrue($filter instanceof Filter\FilterInterface); + } + } + + public function testAdapterShouldAllowRemovingFilterInstancesByClassName() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $this->assertTrue($this->adapter->hasFilter('Zend\Filter\BaseName')); + $this->adapter->removeFilter('Zend\Filter\BaseName'); + $this->assertFalse($this->adapter->hasFilter('Zend\Filter\BaseName')); + } + + public function testAdapterShouldAllowRemovingFilterInstancesByPluginName() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $this->assertTrue($this->adapter->hasFilter('Boolean')); + $this->adapter->removeFilter('Boolean'); + $this->assertFalse($this->adapter->hasFilter('Boolean')); + } + + public function testRemovingNonexistentFilterShouldDoNothing() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $filters = $this->adapter->getFilters(); + $this->assertFalse($this->adapter->hasFilter('Int')); + $this->adapter->removeFilter('Int'); + $this->assertFalse($this->adapter->hasFilter('Int')); + $test = $this->adapter->getFilters(); + $this->assertSame($filters, $test); + } + + public function testAdapterShouldAllowRemovingAllFiltersAtOnce() + { + $this->testAdapterShouldAllowAddingMultipleFiltersAtOnceUsingBothInstancesAndPluginLoader(); + $this->adapter->clearFilters(); + $filters = $this->adapter->getFilters(); + $this->assertTrue(is_array($filters)); + $this->assertEquals(0, count($filters)); + } + + public function testTransferDestinationShouldBeMutable() + { + $directory = __DIR__; + $this->adapter->setDestination($directory); + $destinations = $this->adapter->getDestination(); + $this->assertTrue(is_array($destinations)); + foreach ($destinations as $file => $destination) { + $this->assertEquals($directory, $destination); + } + + $newdirectory = __DIR__ + . DIRECTORY_SEPARATOR . '_files'; + $this->adapter->setDestination($newdirectory, 'foo'); + $this->assertEquals($newdirectory, $this->adapter->getDestination('foo')); + $this->assertEquals($directory, $this->adapter->getDestination('bar')); + } + + public function testAdapterShouldAllowRetrievingDestinationsForAnArrayOfSpecifiedFiles() + { + $this->adapter->setDestination(__DIR__); + $destinations = $this->adapter->getDestination(array('bar', 'baz')); + $this->assertTrue(is_array($destinations)); + $directory = __DIR__; + foreach ($destinations as $file => $destination) { + $this->assertTrue(in_array($file, array('bar', 'baz'))); + $this->assertEquals($directory, $destination); + } + } + + public function testSettingAndRetrievingOptions() + { + $this->assertEquals( + array( + 'bar' => array('ignoreNoFile' => false, 'useByteString' => true), + 'baz' => array('ignoreNoFile' => false, 'useByteString' => true), + 'foo' => array('ignoreNoFile' => false, 'useByteString' => true, 'detectInfos' => true), + 'file_0_' => array('ignoreNoFile' => false, 'useByteString' => true), + 'file_1_' => array('ignoreNoFile' => false, 'useByteString' => true), + ), $this->adapter->getOptions()); + + $this->adapter->setOptions(array('ignoreNoFile' => true)); + $this->assertEquals( + array( + 'bar' => array('ignoreNoFile' => true, 'useByteString' => true), + 'baz' => array('ignoreNoFile' => true, 'useByteString' => true), + 'foo' => array('ignoreNoFile' => true, 'useByteString' => true, 'detectInfos' => true), + 'file_0_' => array('ignoreNoFile' => true, 'useByteString' => true), + 'file_1_' => array('ignoreNoFile' => true, 'useByteString' => true), + ), $this->adapter->getOptions()); + + $this->adapter->setOptions(array('ignoreNoFile' => false), 'foo'); + $this->assertEquals( + array( + 'bar' => array('ignoreNoFile' => true, 'useByteString' => true), + 'baz' => array('ignoreNoFile' => true, 'useByteString' => true), + 'foo' => array('ignoreNoFile' => false, 'useByteString' => true, 'detectInfos' => true), + 'file_0_' => array('ignoreNoFile' => true, 'useByteString' => true), + 'file_1_' => array('ignoreNoFile' => true, 'useByteString' => true), + ), $this->adapter->getOptions()); + } + + public function testGetAllAdditionalFileInfos() + { + $files = $this->adapter->getFileInfo(); + $this->assertEquals(5, count($files)); + $this->assertEquals('baz.text', $files['baz']['name']); + } + + public function testGetAdditionalFileInfosForSingleFile() + { + $files = $this->adapter->getFileInfo('baz'); + $this->assertEquals(1, count($files)); + $this->assertEquals('baz.text', $files['baz']['name']); + } + + public function testGetAdditionalFileInfosForUnknownFile() + { + $this->setExpectedException('Zend\File\Transfer\Exception\RuntimeException', 'The file transfer adapter can not find "unknown"'); + $files = $this->adapter->getFileInfo('unknown'); + } + + public function testAdapterShouldAllowRetrievingFileName() + { + $path = __DIR__ + . DIRECTORY_SEPARATOR . '_files'; + $this->adapter->setDestination($path); + $this->assertEquals($path . DIRECTORY_SEPARATOR . 'foo.jpg', $this->adapter->getFileName('foo')); + } + + public function testAdapterShouldAllowRetrievingFileNameWithoutPath() + { + $path = __DIR__ + . DIRECTORY_SEPARATOR . '_files'; + $this->adapter->setDestination($path); + $this->assertEquals('foo.jpg', $this->adapter->getFileName('foo', false)); + } + + public function testAdapterShouldAllowRetrievingAllFileNames() + { + $path = __DIR__ + . DIRECTORY_SEPARATOR . '_files'; + $this->adapter->setDestination($path); + $files = $this->adapter->getFileName(); + $this->assertTrue(is_array($files)); + $this->assertEquals($path . DIRECTORY_SEPARATOR . 'bar.png', $files['bar']); + } + + public function testAdapterShouldAllowRetrievingAllFileNamesWithoutPath() + { + $path = __DIR__ + . DIRECTORY_SEPARATOR . '_files'; + $this->adapter->setDestination($path); + $files = $this->adapter->getFileName(null, false); + $this->assertTrue(is_array($files)); + $this->assertEquals('bar.png', $files['bar']); + } + + public function testExceptionForUnknownHashValue() + { + $this->setExpectedException('Zend\File\Transfer\Exception\InvalidArgumentException', 'Unknown hash algorithm'); + $this->adapter->getHash('foo', 'unknown_hash'); + } + + public function testIgnoreHashValue() + { + $this->adapter->addInvalidFile(); + $return = $this->adapter->getHash('crc32', 'test'); + $this->assertEquals(array(), $return); + } + + public function testEmptyTempDirectoryDetection() + { + $this->adapter->tmpDir = ""; + $this->assertTrue(empty($this->adapter->tmpDir), "Empty temporary directory"); + } + + public function testTempDirectoryDetection() + { + $this->adapter->getTmpDir(); + $this->assertTrue(!empty($this->adapter->tmpDir), "Temporary directory filled"); + } + + public function testTemporaryDirectoryAccessDetection() + { + $this->adapter->tmpDir = "."; + $path = "/NoPath/To/File"; + $this->assertFalse($this->adapter->isPathWriteable($path)); + $this->assertTrue($this->adapter->isPathWriteable($this->adapter->tmpDir)); + } + + public function testFileSizeButNoFileFound() + { + $this->setExpectedException('Zend\File\Transfer\Exception\InvalidArgumentException', 'does not exist'); + $this->assertEquals(10, $this->adapter->getFileSize()); + } + + public function testIgnoreFileSize() + { + $this->adapter->addInvalidFile(); + $return = $this->adapter->getFileSize('test'); + $this->assertEquals(array(), $return); + } + + public function testFileSizeByTmpName() + { + $expectedSize = sprintf("%.2fkB", 1.14); + $options = $this->adapter->getOptions(); + $this->assertTrue($options['baz']['useByteString']); + $this->assertEquals($expectedSize, $this->adapter->getFileSize('baz.text')); + $this->adapter->setOptions(array('useByteString' => false)); + $options = $this->adapter->getOptions(); + $this->assertFalse($options['baz']['useByteString']); + $this->assertEquals(1172, $this->adapter->getFileSize('baz.text')); + } + + public function testMimeTypeButNoFileFound() + { + $this->setExpectedException('Zend\File\Transfer\Exception\InvalidArgumentException', 'does not exist'); + $this->assertEquals('image/jpeg', $this->adapter->getMimeType()); + } + + public function testIgnoreMimeType() + { + $this->adapter->addInvalidFile(); + $return = $this->adapter->getMimeType('test'); + $this->assertEquals(array(), $return); + } + + public function testMimeTypeByTmpName() + { + $this->assertEquals('text/plain', $this->adapter->getMimeType('baz.text')); + } + + public function testSetOwnErrorMessage() + { + $this->adapter->addValidator('Count', false, array('min' => 5, 'max' => 5, 'messages' => array(FileValidator\Count::TOO_FEW => 'Zu wenige'))); + $this->assertFalse($this->adapter->isValid('foo')); + $message = $this->adapter->getMessages(); + $this->assertContains('Zu wenige', $message); + + $this->setExpectedException('Zend\File\Transfer\Exception\InvalidArgumentException', 'does not exist'); + $this->assertEquals('image/jpeg', $this->adapter->getMimeType()); + } + + public function testTransferDestinationAtNonExistingElement() + { + $directory = __DIR__; + $this->adapter->setDestination($directory, 'nonexisting'); + $this->assertEquals($directory, $this->adapter->getDestination('nonexisting')); + + $this->setExpectedException('Zend\File\Transfer\Exception\InvalidArgumentException', 'not find'); + $this->assertTrue(is_string($this->adapter->getDestination('reallynonexisting'))); + } + + /** + * @ZF-7376 + */ + public function testSettingMagicFile() + { + $this->adapter->setOptions(array('magicFile' => 'test/file')); + $this->assertEquals( + array( + 'bar' => array('magicFile' => 'test/file', 'ignoreNoFile' => false, 'useByteString' => true), + ), $this->adapter->getOptions('bar')); + } + + /** + * @ZF-8693 + */ + public function testAdapterShouldAllowAddingMultipleValidatorsAtOnceUsingBothInstancesAndPluginLoaderForDifferentFiles() + { + $validators = array( + array('MimeType', true, array('image/jpeg')), // no files + array('FilesSize', true, array('max' => '1MB', 'message' => 'файл больше 1MБ')), // no files + array('Count', true, array('min' => 1, 'max' => '1', 'message' => 'файл не 1'), 'bar'), // 'bar' from config + array('MimeType', true, array('image/jpeg'), 'bar'), // 'bar' from config + ); + + $this->adapter->addValidators($validators, 'foo'); // set validators to 'foo' + + $test = $this->adapter->getValidators(); + $this->assertEquals(3, count($test)); + + //test files specific validators + $test = $this->adapter->getValidators('foo'); + $this->assertEquals(2, count($test)); + $mimeType = array_shift($test); + $this->assertTrue($mimeType instanceof FileValidator\MimeType); + $filesSize = array_shift($test); + $this->assertTrue($filesSize instanceof FileValidator\FilesSize); + + $test = $this->adapter->getValidators('bar'); + $this->assertEquals(2, count($test)); + $filesSize = array_shift($test); + $this->assertTrue($filesSize instanceof FileValidator\Count); + $mimeType = array_shift($test); + $this->assertTrue($mimeType instanceof FileValidator\MimeType); + + $test = $this->adapter->getValidators('baz'); + $this->assertEquals(0, count($test)); + } + + /** + * @ZF-9132 + */ + public function testSettingAndRetrievingDetectInfosOption() + { + $this->assertEquals(array( + 'foo' => array( + 'ignoreNoFile' => false, + 'useByteString' => true, + 'detectInfos' => true)) + , $this->adapter->getOptions('foo')); + $this->adapter->setOptions(array('detectInfos' => false)); + $this->assertEquals(array( + 'foo' => array( + 'ignoreNoFile' => false, + 'useByteString' => true, + 'detectInfos' => false)) + , $this->adapter->getOptions('foo')); + } +} diff --git a/test/Transfer/Adapter/HttpTest.php b/test/Transfer/Adapter/HttpTest.php new file mode 100644 index 0000000..4ba2a08 --- /dev/null +++ b/test/Transfer/Adapter/HttpTest.php @@ -0,0 +1,267 @@ + array( + 'name' => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test.txt', + 'type' => 'plain/text', + 'size' => 8, + 'tmp_name' => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test.txt', + 'error' => 0)); + $this->adapter = new HttpTestMockAdapter(); + } + + /** + * Tears down the fixture, for example, close a network connection. + * This method is called after a test is executed. + * + * @return void + */ + public function tearDown() + { + } + + public function testEmptyAdapter() + { + $files = $this->adapter->getFileName(); + $this->assertContains('test.txt', $files); + } + + public function testAutoSetUploadValidator() + { + $validators = array( + new FileValidator\Count(1), + new FileValidator\Extension('jpg'), + ); + $this->adapter->setValidators($validators); + $test = $this->adapter->getValidator('Upload'); + $this->assertTrue($test instanceof FileValidator\Upload); + } + + public function testSendingFiles() + { + $this->setExpectedException('Zend\File\Transfer\Exception\BadMethodCallException', 'not implemented'); + $this->adapter->send(); + } + + public function testFileIsSent() + { + $this->setExpectedException('Zend\File\Transfer\Exception\BadMethodCallException', 'not implemented'); + $this->adapter->isSent(); + } + + public function testFileIsUploaded() + { + $this->assertTrue($this->adapter->isUploaded()); + } + + public function testFileIsNotUploaded() + { + $this->assertFalse($this->adapter->isUploaded('unknownFile')); + } + + public function testFileIsNotFiltered() + { + $this->assertFalse($this->adapter->isFiltered('unknownFile')); + $this->assertFalse($this->adapter->isFiltered()); + } + + public function testFileIsNotReceived() + { + $this->assertFalse($this->adapter->isReceived('unknownFile')); + $this->assertFalse($this->adapter->isReceived()); + } + + public function testReceiveUnknownFile() + { + try { + $this->assertFalse($this->adapter->receive('unknownFile')); + } catch (RuntimeException $e) { + $this->assertContains('not find', $e->getMessage()); + } + } + + public function testReceiveValidatedFile() + { + $_FILES = array( + 'txt' => array( + 'name' => 'unknown.txt', + 'type' => 'plain/text', + 'size' => 8, + 'tmp_name' => 'unknown.txt', + 'error' => 0)); + $adapter = new HttpTestMockAdapter(); + $this->assertFalse($adapter->receive()); + } + + public function testReceiveIgnoredFile() + { + $this->adapter->setOptions(array('ignoreNoFile' => true)); + $this->assertTrue($this->adapter->receive()); + } + + public function testReceiveWithRenameFilter() + { + $this->adapter->addFilter('Rename', array('target' => '/testdir')); + $this->adapter->setOptions(array('ignoreNoFile' => true)); + $this->assertTrue($this->adapter->receive()); + } + + public function testReceiveWithRenameFilterButWithoutDirectory() + { + $this->adapter->setDestination(__DIR__); + $this->adapter->addFilter('Rename', array('overwrite' => false)); + $this->adapter->setOptions(array('ignoreNoFile' => true)); + $this->assertTrue($this->adapter->receive()); + } + + public function testMultiFiles() + { + $_FILES = array( + 'txt' => array( + 'name' => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test.txt', + 'type' => 'plain/text', + 'size' => 8, + 'tmp_name' => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'test.txt', + 'error' => 0), + 'exe' => array( + 'name' => array( + 0 => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'file1.txt', + 1 => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'file2.txt'), + 'type' => array( + 0 => 'plain/text', + 1 => 'plain/text'), + 'size' => array( + 0 => 8, + 1 => 8), + 'tmp_name' => array( + 0 => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'file1.txt', + 1 => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'file2.txt'), + 'error' => array( + 0 => 0, + 1 => 0))); + $adapter = new HttpTestMockAdapter(); + $adapter->setOptions(array('ignoreNoFile' => true)); + $this->assertTrue($adapter->receive('exe')); + $this->assertEquals( + array('exe_0_' => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'file1.txt', + 'exe_1_' => __DIR__ . DIRECTORY_SEPARATOR . '_files' . DIRECTORY_SEPARATOR . 'file2.txt'), + $adapter->getFileName('exe', false)); + } + + public function testNoUploadInProgress() + { + if (!Adapter\Http::isApcAvailable() && !Adapter\Http::isUploadProgressAvailable()) { + $this->markTestSkipped('Whether APC nor UploadExtension available'); + } + + $status = HttpTestMockAdapter::getProgress(); + $this->assertContains('No upload in progress', $status); + } + + public function testUploadProgressFailure() + { + if (!Adapter\Http::isApcAvailable() && !Adapter\Http::isUploadProgressAvailable()) { + $this->markTestSkipped('Whether APC nor UploadExtension available'); + } + + $_GET['progress_key'] = 'mykey'; + $status = HttpTestMockAdapter::getProgress(); + $this->assertEquals(array( + 'total' => 100, + 'current' => 100, + 'rate' => 10, + 'id' => 'mykey', + 'done' => false, + 'message' => '100B - 100B' + ), $status); + + $this->adapter->switchApcToUP(); + $status = HttpTestMockAdapter::getProgress($status); + $this->assertEquals(array( + 'total' => 100, + 'bytes_total' => 100, + 'current' => 100, + 'bytes_uploaded' => 100, + 'rate' => 10, + 'speed_average' => 10, + 'cancel_upload' => true, + 'message' => 'The upload has been canceled', + 'done' => true, + 'id' => 'mykey' + ), $status); + } + + public function testUploadProgressAdapter() + { + if (!Adapter\Http::isApcAvailable() && !Adapter\Http::isUploadProgressAvailable()) { + $this->markTestSkipped('Whether APC nor UploadExtension available'); + } + + $_GET['progress_key'] = 'mykey'; + $adapter = new AdapterProgressBar\Console(); + $status = array('progress' => $adapter, 'session' => 'upload'); + $status = HttpTestMockAdapter::getProgress($status); + $this->assertTrue(array_key_exists('total', $status)); + $this->assertTrue(array_key_exists('current', $status)); + $this->assertTrue(array_key_exists('rate', $status)); + $this->assertTrue(array_key_exists('id', $status)); + $this->assertTrue(array_key_exists('message', $status)); + $this->assertTrue(array_key_exists('progress', $status)); + $this->assertTrue($status['progress'] instanceof ProgressBar\ProgressBar); + + $this->adapter->switchApcToUP(); + $status = HttpTestMockAdapter::getProgress($status); + $this->assertTrue(array_key_exists('total', $status)); + $this->assertTrue(array_key_exists('current', $status)); + $this->assertTrue(array_key_exists('rate', $status)); + $this->assertTrue(array_key_exists('id', $status)); + $this->assertTrue(array_key_exists('message', $status)); + $this->assertTrue(array_key_exists('progress', $status)); + $this->assertTrue($status['progress'] instanceof ProgressBar\ProgressBar); + } + + public function testValidationOfPhpExtendsFormError() + { + $_SERVER['CONTENT_LENGTH'] = 10; + + $_FILES = array(); + $adapter = new HttpTestMockAdapter(); + $this->assertFalse($adapter->isValidParent()); + $this->assertContains('exceeds the defined ini size', current($adapter->getMessages())); + } +} diff --git a/test/Transfer/Adapter/HttpTestMockAdapter.php b/test/Transfer/Adapter/HttpTestMockAdapter.php new file mode 100644 index 0000000..f44b202 --- /dev/null +++ b/test/Transfer/Adapter/HttpTestMockAdapter.php @@ -0,0 +1,61 @@ + 100, 'current' => 100, 'rate' => 10); + } + + public static function uPTest($id) + { + return array('bytes_total' => 100, 'bytes_uploaded' => 100, 'speed_average' => 10, 'cancel_upload' => true); + } + + public function switchApcToUP() + { + self::$callbackApc = null; + self::$callbackUploadProgress = array('ZendTest\File\Transfer\Adapter\HttpTestMockAdapter', 'uPTest'); + } +} diff --git a/test/Transfer/Adapter/_files/file1.txt b/test/Transfer/Adapter/_files/file1.txt new file mode 100644 index 0000000..e7cbb71 --- /dev/null +++ b/test/Transfer/Adapter/_files/file1.txt @@ -0,0 +1 @@ +testfile \ No newline at end of file diff --git a/test/Transfer/Adapter/_files/file2.txt b/test/Transfer/Adapter/_files/file2.txt new file mode 100644 index 0000000..e7cbb71 --- /dev/null +++ b/test/Transfer/Adapter/_files/file2.txt @@ -0,0 +1 @@ +testfile \ No newline at end of file diff --git a/test/Transfer/Adapter/_files/test.txt b/test/Transfer/Adapter/_files/test.txt new file mode 100644 index 0000000..e7cbb71 --- /dev/null +++ b/test/Transfer/Adapter/_files/test.txt @@ -0,0 +1 @@ +testfile \ No newline at end of file