composer package updates
[openemr.git] / vendor / zendframework / zend-file / src / Transfer / Adapter / AbstractAdapter.php
blobbc90dfe02615f3f1e3df0f807584405d967fda1e
1 <?php
2 /**
3 * Zend Framework (http://framework.zend.com/)
5 * @link http://github.com/zendframework/zf2 for the canonical source repository
6 * @copyright Copyright (c) 2005-2015 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
10 namespace Zend\File\Transfer\Adapter;
12 use ErrorException;
13 use ReflectionClass;
14 use Zend\File\Transfer;
15 use Zend\File\Transfer\Exception;
16 use Zend\Filter;
17 use Zend\Filter\Exception as FilterException;
18 use Zend\I18n\Translator\TranslatorInterface as Translator;
19 use Zend\I18n\Translator\TranslatorAwareInterface;
20 use Zend\ServiceManager\ServiceManager;
21 use Zend\Stdlib\ErrorHandler;
22 use Zend\Validator;
24 /**
25 * Abstract class for file transfers (Downloads and Uploads)
27 * This class needs a full rewrite. It re-implements functionality present in
28 * Zend\Filter\Input and/or Zend\Form\Element, and in a way that's inconsistent
29 * with either one. Additionally, plugin loader usage is now deprecated -- but
30 * modifying that should be done in tandem with a rewrite to utilize validator
31 * and filter chains instead.
33 * @deprecated since 2.7.0, and scheduled for removal with 3.0.0
35 abstract class AbstractAdapter implements TranslatorAwareInterface
37 /**@+
38 * Plugin loader Constants
40 const FILTER = 'FILTER';
41 const VALIDATOR = 'VALIDATOR';
42 /**@-*/
44 /**
45 * Internal list of breaks
47 * @var array
49 protected $break = [];
51 /**
52 * @var FilterPluginManager
54 protected $filterManager;
56 /**
57 * Internal list of filters
59 * @var array
61 protected $filters = [];
63 /**
64 * Plugin loaders for filter and validation chains
66 * @var array
68 protected $loaders = [];
70 /**
71 * Internal list of messages
73 * @var array
75 protected $messages = [];
77 /**
78 * @var Translator
80 protected $translator;
82 /**
83 * Is translation enabled?
85 * @var bool
87 protected $translatorEnabled = true;
89 /**
90 * Translator text domain (optional)
92 * @var string
94 protected $translatorTextDomain = 'default';
96 /**
97 * @var ValidatorPluginManager
99 protected $validatorManager;
102 * Internal list of validators
103 * @var array
105 protected $validators = [];
108 * Internal list of files
109 * This array looks like this:
110 * array(form => array( - Form is the name within the form or, if not set the filename
111 * name, - Original name of this file
112 * type, - Mime type of this file
113 * size, - Filesize in bytes
114 * tmp_name, - Internally temporary filename for uploaded files
115 * error, - Error which has occurred
116 * destination, - New destination for this file
117 * validators, - Set validator names for this file
118 * files - Set file names for this file
119 * ))
121 * @var array
123 protected $files = [];
126 * TMP directory
127 * @var string
129 protected $tmpDir;
132 * Available options for file transfers
134 protected $options = [
135 'ignoreNoFile' => false,
136 'useByteString' => true,
137 'magicFile' => null,
138 'detectInfos' => true,
142 * Send file
144 * @param mixed $options
145 * @return bool
147 abstract public function send($options = null);
150 * Receive file
152 * @param mixed $options
153 * @return bool
155 abstract public function receive($options = null);
158 * Is file sent?
160 * @param array|string|null $files
161 * @return bool
163 abstract public function isSent($files = null);
166 * Is file received?
168 * @param array|string|null $files
169 * @return bool
171 abstract public function isReceived($files = null);
174 * Has a file been uploaded ?
176 * @param array|string|null $files
177 * @return bool
179 abstract public function isUploaded($files = null);
182 * Has the file been filtered ?
184 * @param array|string|null $files
185 * @return bool
187 abstract public function isFiltered($files = null);
190 * Adds one or more files
192 * @param string|array $file File to add
193 * @param string|array $validator Validators to use for this file, must be set before
194 * @param string|array $filter Filters to use for this file, must be set before
195 * @return AbstractAdapter
196 * @throws Exception Not implemented
198 //abstract public function addFile($file, $validator = null, $filter = null);
201 * Returns all set types
203 * @return array List of set types
204 * @throws Exception Not implemented
206 //abstract public function getType();
209 * Adds one or more type of files
211 * @param string|array $type Type of files to add
212 * @param string|array $validator Validators to use for this file, must be set before
213 * @param string|array $filter Filters to use for this file, must be set before
214 * @return AbstractAdapter
215 * @throws Exception Not implemented
217 //abstract public function addType($type, $validator = null, $filter = null);
220 * Returns all set files
222 * @return array List of set files
224 //abstract public function getFile();
227 * Set the filter plugin manager instance
229 * @param FilterPluginManager $filterManager
230 * @return AbstractAdapter
232 public function setFilterManager(FilterPluginManager $filterManager)
234 $this->filterManager = $filterManager;
235 return $this;
239 * Get the filter plugin manager instance
241 * @return FilterPluginManager
243 public function getFilterManager()
245 if (! $this->filterManager instanceof FilterPluginManager) {
246 $this->setFilterManager(new FilterPluginManager(new ServiceManager()));
248 return $this->filterManager;
252 * Set the validator plugin manager instance
254 * @param ValidatorPluginManager $validatorManager
255 * @return AbstractAdapter
257 public function setValidatorManager(ValidatorPluginManager $validatorManager)
259 $this->validatorManager = $validatorManager;
260 return $this;
264 * Get the validator plugin manager instance
266 * @return ValidatorPluginManager
268 public function getValidatorManager()
270 if (! $this->validatorManager instanceof ValidatorPluginManager) {
271 if ($this->isServiceManagerV3()) {
272 $this->setValidatorManager(new ValidatorPluginManager(new ServiceManager()));
273 } else {
274 $this->setValidatorManager(new ValidatorPluginManager());
277 return $this->validatorManager;
281 * Adds a new validator for this class
283 * @param string|Validator\ValidatorInterface $validator Type of validator to add
284 * @param bool $breakChainOnFailure If the validation chain should stop a failure
285 * @param string|array $options Options to set for the validator
286 * @param string|array $files Files to limit this validator to
287 * @return AbstractAdapter
288 * @throws Exception\InvalidArgumentException for invalid type
290 public function addValidator($validator, $breakChainOnFailure = false, $options = null, $files = null)
292 if (is_string($validator)) {
293 $options = (null !== $options && is_scalar($options)) ? [$options] : $options;
294 $validator = $this->getValidatorManager()->get($validator, $options);
295 if (is_array($options) && isset($options['messages'])) {
296 if (is_array($options['messages'])) {
297 $validator->setMessages($options['messages']);
298 } elseif (is_string($options['messages'])) {
299 $validator->setMessage($options['messages']);
302 unset($options['messages']);
306 if (! $validator instanceof Validator\ValidatorInterface) {
307 throw new Exception\InvalidArgumentException(
308 'Invalid validator provided to addValidator; ' .
309 'must be string or Zend\Validator\ValidatorInterface'
313 $name = get_class($validator);
315 $this->validators[$name] = $validator;
316 $this->break[$name] = $breakChainOnFailure;
317 $files = $this->getFiles($files, true, true);
318 foreach ($files as $file) {
319 if ($name == 'NotEmpty') {
320 $temp = $this->files[$file]['validators'];
321 $this->files[$file]['validators'] = [$name];
322 $this->files[$file]['validators'] += $temp;
323 } else {
324 $this->files[$file]['validators'][] = $name;
327 $this->files[$file]['validated'] = false;
330 return $this;
334 * Add Multiple validators at once
336 * @param array $validators
337 * @param string|array $files
338 * @return AbstractAdapter
339 * @throws Exception\InvalidArgumentException for invalid type
341 public function addValidators(array $validators, $files = null)
343 foreach ($validators as $name => $validatorInfo) {
344 if ($validatorInfo instanceof Validator\ValidatorInterface) {
345 $this->addValidator($validatorInfo, null, null, $files);
346 } elseif (is_string($validatorInfo)) {
347 if (! is_int($name)) {
348 $this->addValidator($name, null, $validatorInfo, $files);
349 } else {
350 $this->addValidator($validatorInfo, null, null, $files);
352 } elseif (is_array($validatorInfo)) {
353 $argc = count($validatorInfo);
354 $breakChainOnFailure = false;
355 $options = [];
356 if (isset($validatorInfo['validator'])) {
357 $validator = $validatorInfo['validator'];
358 if (isset($validatorInfo['breakChainOnFailure'])) {
359 $breakChainOnFailure = $validatorInfo['breakChainOnFailure'];
362 if (isset($validatorInfo['options'])) {
363 $options = $validatorInfo['options'];
366 $this->addValidator($validator, $breakChainOnFailure, $options, $files);
367 } else {
368 if (is_string($name)) {
369 $validator = $name;
370 $options = $validatorInfo;
371 $this->addValidator($validator, $breakChainOnFailure, $options, $files);
372 } else {
373 $file = $files;
374 switch (true) {
375 case (0 == $argc):
376 break;
377 case (1 <= $argc):
378 $validator = array_shift($validatorInfo);
379 // fall-through
380 case (2 <= $argc):
381 $breakChainOnFailure = array_shift($validatorInfo);
382 // fall-through
383 case (3 <= $argc):
384 $options = array_shift($validatorInfo);
385 // fall-through
386 case (4 <= $argc):
387 if (! empty($validatorInfo)) {
388 $file = array_shift($validatorInfo);
390 // fall-through
391 default:
392 $this->addValidator($validator, $breakChainOnFailure, $options, $file);
393 break;
397 } else {
398 throw new Exception\InvalidArgumentException('Invalid validator passed to addValidators()');
402 return $this;
406 * Sets a validator for the class, erasing all previous set
408 * @param array $validators Validators to set
409 * @param string|array $files Files to limit this validator to
410 * @return AbstractAdapter
412 public function setValidators(array $validators, $files = null)
414 $this->clearValidators();
415 return $this->addValidators($validators, $files);
419 * Determine if a given validator has already been registered
421 * @param string $name
422 * @return bool
424 public function hasValidator($name)
426 return (false !== $this->getValidatorIdentifier($name));
430 * Retrieve individual validator
432 * @param string $name
433 * @return Validator\ValidatorInterface|null
435 public function getValidator($name)
437 if (false === ($identifier = $this->getValidatorIdentifier($name))) {
438 return;
440 return $this->validators[$identifier];
444 * Returns all set validators
446 * @param string|array $files (Optional) Returns the validator for this files
447 * @return null|array List of set validators
449 public function getValidators($files = null)
451 if ($files === null) {
452 return $this->validators;
455 $files = $this->getFiles($files, true, true);
456 $validators = [];
457 foreach ($files as $file) {
458 if (! empty($this->files[$file]['validators'])) {
459 $validators += $this->files[$file]['validators'];
463 $validators = array_unique($validators);
464 $result = [];
465 foreach ($validators as $validator) {
466 $result[$validator] = $this->validators[$validator];
469 return $result;
473 * Remove an individual validator
475 * @param string $name
476 * @return AbstractAdapter
478 public function removeValidator($name)
480 if (false === ($key = $this->getValidatorIdentifier($name))) {
481 return $this;
484 unset($this->validators[$key]);
485 foreach (array_keys($this->files) as $file) {
486 if (empty($this->files[$file]['validators'])) {
487 continue;
490 $index = array_search($key, $this->files[$file]['validators']);
491 if ($index === false) {
492 continue;
495 unset($this->files[$file]['validators'][$index]);
496 $this->files[$file]['validated'] = false;
499 return $this;
503 * Remove all validators
505 * @return AbstractAdapter
507 public function clearValidators()
509 $this->validators = [];
510 foreach (array_keys($this->files) as $file) {
511 $this->files[$file]['validators'] = [];
512 $this->files[$file]['validated'] = false;
515 return $this;
519 * Sets Options for adapters
521 * @param array $options Options to set
522 * @param array $files (Optional) Files to set the options for
523 * @return AbstractAdapter
525 public function setOptions($options = [], $files = null)
527 $file = $this->getFiles($files, false, true);
529 if (is_array($options)) {
530 if (empty($file)) {
531 $this->options = array_merge($this->options, $options);
534 foreach ($options as $name => $value) {
535 foreach ($file as $key => $content) {
536 switch ($name) {
537 case 'magicFile':
538 $this->files[$key]['options'][$name] = (string) $value;
539 break;
541 case 'ignoreNoFile':
542 case 'useByteString':
543 case 'detectInfos':
544 $this->files[$key]['options'][$name] = (bool) $value;
545 break;
547 default:
548 continue;
554 return $this;
558 * Returns set options for adapters or files
560 * @param array $files (Optional) Files to return the options for
561 * @return array Options for given files
563 public function getOptions($files = null)
565 $file = $this->getFiles($files, false, true);
567 foreach ($file as $key => $content) {
568 if (isset($this->files[$key]['options'])) {
569 $options[$key] = $this->files[$key]['options'];
570 } else {
571 $options[$key] = [];
575 return $options;
579 * Checks if the files are valid
581 * @param string|array $files (Optional) Files to check
582 * @return bool True if all checks are valid
584 public function isValid($files = null)
586 $check = $this->getFiles($files, false, true);
587 if (empty($check)) {
588 return false;
591 $translator = $this->getTranslator();
592 $this->messages = [];
593 $break = false;
594 foreach ($check as $content) {
595 if (array_key_exists('validators', $content) &&
596 in_array(Validator\File\Count::class, $content['validators'])) {
597 $validator = $this->validators[Validator\File\Count::class];
598 $count = $content;
599 if (empty($content['tmp_name'])) {
600 continue;
603 if (array_key_exists('destination', $content)) {
604 $checkit = $content['destination'];
605 } else {
606 $checkit = dirname($content['tmp_name']);
609 $checkit .= DIRECTORY_SEPARATOR . $content['name'];
610 $validator->addFile($checkit);
614 if (isset($count)) {
615 if (! $validator->isValid($count['tmp_name'], $count)) {
616 $this->messages += $validator->getMessages();
620 foreach ($check as $key => $content) {
621 $fileerrors = [];
622 if (array_key_exists('validators', $content) && $content['validated']) {
623 continue;
626 if (array_key_exists('validators', $content)) {
627 foreach ($content['validators'] as $class) {
628 $validator = $this->validators[$class];
629 if (method_exists($validator, 'setTranslator')) {
630 $validator->setTranslator($translator);
633 if (($class === 'Zend\Validator\File\Upload') && (empty($content['tmp_name']))) {
634 $tocheck = $key;
635 } else {
636 $tocheck = $content['tmp_name'];
639 if (! $validator->isValid($tocheck, $content)) {
640 $fileerrors += $validator->getMessages();
643 if (! empty($content['options']['ignoreNoFile']) && (isset($fileerrors['fileUploadErrorNoFile']))) {
644 unset($fileerrors['fileUploadErrorNoFile']);
645 break;
648 if (($class === 'Zend\Validator\File\Upload') && (count($fileerrors) > 0)) {
649 break;
652 if (($this->break[$class]) && (count($fileerrors) > 0)) {
653 $break = true;
654 break;
659 if (count($fileerrors) > 0) {
660 $this->files[$key]['validated'] = false;
661 } else {
662 $this->files[$key]['validated'] = true;
665 $this->messages += $fileerrors;
666 if ($break) {
667 break;
671 if (count($this->messages) > 0) {
672 return false;
675 return true;
679 * Returns found validation messages
681 * @return array
683 public function getMessages()
685 return $this->messages;
689 * Retrieve error codes
691 * @return array
693 public function getErrors()
695 return array_keys($this->messages);
699 * Are there errors registered?
701 * @return bool
703 public function hasErrors()
705 return (! empty($this->messages));
709 * Adds a new filter for this class
711 * @param string|Filter\FilterInterface $filter Type of filter to add
712 * @param string|array $options Options to set for the filter
713 * @param string|array $files Files to limit this filter to
714 * @return AbstractAdapter
715 * @throws Exception\InvalidArgumentException for invalid type
717 public function addFilter($filter, $options = null, $files = null)
719 if (is_string($filter)) {
720 $options = (null !== $options && is_scalar($options)) ? [$options] : $options;
721 $filter = $this->getFilterManager()->get($filter, $options);
724 if (! $filter instanceof Filter\FilterInterface) {
725 throw new Exception\InvalidArgumentException('Invalid filter specified');
728 $class = get_class($filter);
729 $this->filters[$class] = $filter;
730 $files = $this->getFiles($files, true, true);
731 foreach ($files as $file) {
732 $this->files[$file]['filters'][] = $class;
735 return $this;
739 * Add Multiple filters at once
741 * @param array $filters
742 * @param string|array $files
743 * @return AbstractAdapter
745 public function addFilters(array $filters, $files = null)
747 foreach ($filters as $key => $spec) {
748 if ($spec instanceof Filter\FilterInterface) {
749 $this->addFilter($spec, null, $files);
750 continue;
753 if (is_string($key)) {
754 $this->addFilter($key, $spec, $files);
755 continue;
758 if (is_int($key)) {
759 if (is_string($spec)) {
760 $this->addFilter($spec, null, $files);
761 continue;
764 if (is_array($spec)) {
765 if (! array_key_exists('filter', $spec)) {
766 continue;
769 $filter = $spec['filter'];
770 unset($spec['filter']);
771 $this->addFilter($filter, $spec, $files);
772 continue;
775 continue;
779 return $this;
783 * Sets a filter for the class, erasing all previous set
785 * @param array $filters Filter to set
786 * @param string|array $files Files to limit this filter to
787 * @return Filter\AbstractFilter
789 public function setFilters(array $filters, $files = null)
791 $this->clearFilters();
792 return $this->addFilters($filters, $files);
796 * Determine if a given filter has already been registered
798 * @param string $name
799 * @return bool
801 public function hasFilter($name)
803 return (false !== $this->getFilterIdentifier($name));
807 * Retrieve individual filter
809 * @param string $name
810 * @return Filter\FilterInterface|null
812 public function getFilter($name)
814 if (false === ($identifier = $this->getFilterIdentifier($name))) {
815 return;
818 return $this->filters[$identifier];
822 * Returns all set filters
824 * @param string|array $files (Optional) Returns the filter for this files
825 * @return array List of set filters
826 * @throws Exception\RuntimeException When file not found
828 public function getFilters($files = null)
830 if ($files === null) {
831 return $this->filters;
834 $files = $this->getFiles($files, true, true);
835 $filters = [];
836 foreach ($files as $file) {
837 if (! empty($this->files[$file]['filters'])) {
838 $filters += $this->files[$file]['filters'];
842 $filters = array_unique($filters);
843 $result = [];
844 foreach ($filters as $filter) {
845 $result[] = $this->filters[$filter];
848 return $result;
852 * Remove an individual filter
854 * @param string $name
855 * @return AbstractAdapter
857 public function removeFilter($name)
859 if (false === ($key = $this->getFilterIdentifier($name))) {
860 return $this;
863 unset($this->filters[$key]);
864 foreach (array_keys($this->files) as $file) {
865 if (empty($this->files[$file]['filters'])) {
866 continue;
869 $index = array_search($key, $this->files[$file]['filters']);
870 if ($index === false) {
871 continue;
874 unset($this->files[$file]['filters'][$index]);
876 return $this;
880 * Remove all filters
882 * @return AbstractAdapter
884 public function clearFilters()
886 $this->filters = [];
887 foreach (array_keys($this->files) as $file) {
888 $this->files[$file]['filters'] = [];
891 return $this;
895 * Retrieves the filename of transferred files.
897 * @param string $file (Optional) Element to return the filename for
898 * @param bool $path (Optional) Should the path also be returned ?
899 * @return string|array
901 public function getFileName($file = null, $path = true)
903 $files = $this->getFiles($file, true, true);
904 $result = [];
905 $directory = "";
906 foreach ($files as $file) {
907 if (empty($this->files[$file]['name'])) {
908 continue;
911 if ($path === true) {
912 $directory = $this->getDestination($file) . DIRECTORY_SEPARATOR;
915 $result[$file] = $directory . $this->files[$file]['name'];
918 if (count($result) == 1) {
919 return current($result);
922 return $result;
926 * Retrieve additional internal file information for files
928 * @param string $file (Optional) File to get information for
929 * @return array
931 public function getFileInfo($file = null)
933 return $this->getFiles($file);
937 * Sets a new destination for the given files
939 * @deprecated Will be changed to be a filter!!!
940 * @param string $destination New destination directory
941 * @param string|array $files Files to set the new destination for
942 * @return AbstractAdapter
943 * @throws Exception\InvalidArgumentException when the given destination is not a directory or does not exist
945 public function setDestination($destination, $files = null)
947 $orig = $files;
948 $destination = rtrim($destination, "/\\");
949 if (! is_dir($destination)) {
950 throw new Exception\InvalidArgumentException('The given destination is not a directory or does not exist');
953 if (! is_writable($destination)) {
954 throw new Exception\InvalidArgumentException('The given destination is not writeable');
957 if ($files === null) {
958 foreach ($this->files as $file => $content) {
959 $this->files[$file]['destination'] = $destination;
961 } else {
962 $files = $this->getFiles($files, true, true);
963 if (empty($files) and is_string($orig)) {
964 $this->files[$orig]['destination'] = $destination;
967 foreach ($files as $file) {
968 $this->files[$file]['destination'] = $destination;
972 return $this;
976 * Retrieve destination directory value
978 * @param null|string|array $files
979 * @throws Exception\InvalidArgumentException
980 * @return null|string|array
982 public function getDestination($files = null)
984 $orig = $files;
985 $files = $this->getFiles($files, false, true);
986 $destinations = [];
987 if (empty($files) and is_string($orig)) {
988 if (isset($this->files[$orig]['destination'])) {
989 $destinations[$orig] = $this->files[$orig]['destination'];
990 } else {
991 throw new Exception\InvalidArgumentException(
992 sprintf('The file transfer adapter can not find "%s"', $orig)
997 foreach ($files as $key => $content) {
998 if (isset($this->files[$key]['destination'])) {
999 $destinations[$key] = $this->files[$key]['destination'];
1000 } else {
1001 $tmpdir = $this->getTmpDir();
1002 $this->setDestination($tmpdir, $key);
1003 $destinations[$key] = $tmpdir;
1007 if (empty($destinations)) {
1008 $destinations = $this->getTmpDir();
1009 } elseif (count($destinations) == 1) {
1010 $destinations = current($destinations);
1013 return $destinations;
1017 * Sets translator to use in helper
1019 * @param Translator $translator [optional] translator.
1020 * Default is null, which sets no translator.
1021 * @param string $textDomain [optional] text domain
1022 * Default is null, which skips setTranslatorTextDomain
1023 * @return AbstractAdapter
1025 public function setTranslator(Translator $translator = null, $textDomain = null)
1027 $this->translator = $translator;
1028 if (null !== $textDomain) {
1029 $this->setTranslatorTextDomain($textDomain);
1031 return $this;
1035 * Retrieve localization translator object
1037 * @return Translator|null
1039 public function getTranslator()
1041 if ($this->isTranslatorEnabled()) {
1042 return;
1045 return $this->translator;
1049 * Checks if the helper has a translator
1051 * @return bool
1053 public function hasTranslator()
1055 return (bool) $this->getTranslator();
1059 * Indicate whether or not translation should be enabled
1061 * @param bool $flag
1062 * @return AbstractAdapter
1064 public function setTranslatorEnabled($flag = true)
1066 $this->translatorEnabled = (bool) $flag;
1067 return $this;
1071 * Is translation enabled?
1073 * @return bool
1075 public function isTranslatorEnabled()
1077 return $this->translatorEnabled;
1081 * Set translation text domain
1083 * @param string $textDomain
1084 * @return AbstractAdapter
1086 public function setTranslatorTextDomain($textDomain = 'default')
1088 $this->translatorTextDomain = $textDomain;
1089 return $this;
1093 * Return the translation text domain
1095 * @return string
1097 public function getTranslatorTextDomain()
1099 return $this->translatorTextDomain;
1103 * Returns the hash for a given file
1105 * @param string $hash Hash algorithm to use
1106 * @param string|array $files Files to return the hash for
1107 * @return string|array Hashstring
1108 * @throws Exception\InvalidArgumentException On unknown hash algorithm
1110 public function getHash($hash = 'crc32', $files = null)
1112 if (! in_array($hash, hash_algos())) {
1113 throw new Exception\InvalidArgumentException('Unknown hash algorithm');
1116 $files = $this->getFiles($files);
1117 $result = [];
1118 foreach ($files as $key => $value) {
1119 if (file_exists($value['name'])) {
1120 $result[$key] = hash_file($hash, $value['name']);
1121 } elseif (file_exists($value['tmp_name'])) {
1122 $result[$key] = hash_file($hash, $value['tmp_name']);
1123 } elseif (empty($value['options']['ignoreNoFile'])) {
1124 throw new Exception\InvalidArgumentException("The file '{$value['name']}' does not exist");
1128 if (count($result) == 1) {
1129 return current($result);
1132 return $result;
1136 * Returns the real filesize of the file
1138 * @param string|array $files Files to get the filesize from
1139 * @return string|array Filesize
1140 * @throws Exception\InvalidArgumentException When the file does not exist
1142 public function getFileSize($files = null)
1144 $files = $this->getFiles($files);
1145 $result = [];
1146 foreach ($files as $key => $value) {
1147 if (file_exists($value['name']) || file_exists($value['tmp_name'])) {
1148 if ($value['options']['useByteString']) {
1149 $result[$key] = static::toByteString($value['size']);
1150 } else {
1151 $result[$key] = $value['size'];
1153 } elseif (empty($value['options']['ignoreNoFile'])) {
1154 throw new Exception\InvalidArgumentException("The file '{$value['name']}' does not exist");
1155 } else {
1156 continue;
1160 if (count($result) == 1) {
1161 return current($result);
1164 return $result;
1168 * Internal method to detect the size of a file
1170 * @param array $value File infos
1171 * @return string Filesize of given file
1173 protected function detectFileSize($value)
1175 if (file_exists($value['name'])) {
1176 $filename = $value['name'];
1177 } elseif (file_exists($value['tmp_name'])) {
1178 $filename = $value['tmp_name'];
1179 } else {
1180 return;
1183 ErrorHandler::start();
1184 $filesize = filesize($filename);
1185 $return = ErrorHandler::stop();
1186 if ($return instanceof ErrorException) {
1187 $filesize = 0;
1190 return sprintf("%u", $filesize);
1194 * Returns the real mimetype of the file
1195 * Uses fileinfo, when not available mime_magic and as last fallback a manual given mimetype
1197 * @param string|array $files Files to get the mimetype from
1198 * @return string|array MimeType
1199 * @throws Exception\InvalidArgumentException When the file does not exist
1201 public function getMimeType($files = null)
1203 $files = $this->getFiles($files);
1204 $result = [];
1205 foreach ($files as $key => $value) {
1206 if (file_exists($value['name']) || file_exists($value['tmp_name'])) {
1207 $result[$key] = $value['type'];
1208 } elseif (empty($value['options']['ignoreNoFile'])) {
1209 throw new Exception\InvalidArgumentException("the file '{$value['name']}' does not exist");
1210 } else {
1211 continue;
1215 if (count($result) == 1) {
1216 return current($result);
1219 return $result;
1223 * Internal method to detect the mime type of a file
1225 * @param array $value File infos
1226 * @return string Mimetype of given file
1228 protected function detectMimeType($value)
1230 if (file_exists($value['name'])) {
1231 $file = $value['name'];
1232 } elseif (file_exists($value['tmp_name'])) {
1233 $file = $value['tmp_name'];
1234 } else {
1235 return;
1238 if (class_exists('finfo', false)) {
1239 if (! empty($value['options']['magicFile'])) {
1240 ErrorHandler::start();
1241 $mime = finfo_open(FILEINFO_MIME_TYPE, $value['options']['magicFile']);
1242 ErrorHandler::stop();
1245 if (empty($mime)) {
1246 ErrorHandler::start();
1247 $mime = finfo_open(FILEINFO_MIME_TYPE);
1248 ErrorHandler::stop();
1251 if (! empty($mime)) {
1252 $result = finfo_file($mime, $file);
1255 unset($mime);
1258 if (empty($result) && (function_exists('mime_content_type')
1259 && ini_get('mime_magic.magicfile'))) {
1260 $result = mime_content_type($file);
1263 if (empty($result)) {
1264 $result = 'application/octet-stream';
1267 return $result;
1271 * Returns the formatted size
1273 * @param int $size
1274 * @return string
1276 protected static function toByteString($size)
1278 $sizes = ['B', 'kB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
1279 for ($i = 0; $size >= 1024 && $i < 9; $i++) {
1280 $size /= 1024;
1283 return round($size, 2) . $sizes[$i];
1287 * Internal function to filter all given files
1289 * @param string|array $files (Optional) Files to check
1290 * @return bool False on error
1292 protected function filter($files = null)
1294 $check = $this->getFiles($files);
1295 foreach ($check as $name => $content) {
1296 if (array_key_exists('filters', $content)) {
1297 foreach ($content['filters'] as $class) {
1298 $filter = $this->filters[$class];
1299 try {
1300 $result = $filter->filter($this->getFileName($name));
1302 $this->files[$name]['destination'] = dirname($result);
1303 $this->files[$name]['name'] = basename($result);
1304 } catch (FilterException\ExceptionInterface $e) {
1305 $this->messages += [$e->getMessage()];
1311 if (count($this->messages) > 0) {
1312 return false;
1315 return true;
1319 * Determine system TMP directory and detect if we have read access
1321 * @return string
1322 * @throws Exception\RuntimeException if unable to determine directory
1324 protected function getTmpDir()
1326 if (null === $this->tmpDir) {
1327 $tmpdir = [];
1328 if (function_exists('sys_get_temp_dir')) {
1329 $tmpdir[] = sys_get_temp_dir();
1332 if (! empty($_ENV['TMP'])) {
1333 $tmpdir[] = realpath($_ENV['TMP']);
1336 if (! empty($_ENV['TMPDIR'])) {
1337 $tmpdir[] = realpath($_ENV['TMPDIR']);
1340 if (! empty($_ENV['TEMP'])) {
1341 $tmpdir[] = realpath($_ENV['TEMP']);
1344 $upload = ini_get('upload_tmp_dir');
1345 if ($upload) {
1346 $tmpdir[] = realpath($upload);
1349 foreach ($tmpdir as $directory) {
1350 if ($this->isPathWriteable($directory)) {
1351 $this->tmpDir = $directory;
1355 if (empty($this->tmpDir)) {
1356 // Attemp to detect by creating a temporary file
1357 $tempFile = tempnam(md5(uniqid(rand(), true)), '');
1358 if ($tempFile) {
1359 $this->tmpDir = realpath(dirname($tempFile));
1360 unlink($tempFile);
1361 } else {
1362 throw new Exception\RuntimeException('Could not determine a temporary directory');
1366 $this->tmpDir = rtrim($this->tmpDir, "/\\");
1368 return $this->tmpDir;
1372 * Tries to detect if we can read and write to the given path
1374 * @param string $path
1375 * @return bool
1377 protected function isPathWriteable($path)
1379 $tempFile = rtrim($path, "/\\");
1380 $tempFile .= '/' . 'test.1';
1382 ErrorHandler::start();
1383 $result = file_put_contents($tempFile, 'TEST');
1384 ErrorHandler::stop();
1386 if ($result == false) {
1387 return false;
1390 ErrorHandler::start();
1391 $result = unlink($tempFile);
1392 ErrorHandler::stop();
1394 if ($result == false) {
1395 return false;
1398 return true;
1402 * Returns found files based on internal file array and given files
1404 * @param string|array $files (Optional) Files to return
1405 * @param bool $names (Optional) Returns only names on true, else complete info
1406 * @param bool $noexception (Optional) Allows throwing an exception, otherwise returns an empty array
1407 * @return array Found files
1408 * @throws Exception\RuntimeException On false filename
1410 protected function getFiles($files, $names = false, $noexception = false)
1412 $check = [];
1414 if (is_string($files)) {
1415 $files = [$files];
1418 if (is_array($files)) {
1419 foreach ($files as $find) {
1420 $found = [];
1421 foreach ($this->files as $file => $content) {
1422 if (! isset($content['name'])) {
1423 continue;
1426 if (($content['name'] === $find) && isset($content['multifiles'])) {
1427 foreach ($content['multifiles'] as $multifile) {
1428 $found[] = $multifile;
1430 break;
1433 if ($file === $find) {
1434 $found[] = $file;
1435 break;
1438 if ($content['name'] === $find) {
1439 $found[] = $file;
1440 break;
1444 if (empty($found)) {
1445 if ($noexception !== false) {
1446 return [];
1449 throw new Exception\RuntimeException(sprintf('The file transfer adapter can not find "%s"', $find));
1452 foreach ($found as $checked) {
1453 $check[$checked] = $this->files[$checked];
1458 if ($files === null) {
1459 $check = $this->files;
1460 $keys = array_keys($check);
1461 foreach ($keys as $key) {
1462 if (isset($check[$key]['multifiles'])) {
1463 unset($check[$key]);
1468 if ($names) {
1469 $check = array_keys($check);
1472 return $check;
1476 * Retrieve internal identifier for a named validator
1478 * @param string $name
1479 * @return string
1481 protected function getValidatorIdentifier($name)
1483 if (array_key_exists($name, $this->validators)) {
1484 return $name;
1487 foreach (array_keys($this->validators) as $test) {
1488 if (preg_match('/' . preg_quote($name) . '$/i', $test)) {
1489 return $test;
1493 return false;
1497 * Retrieve internal identifier for a named filter
1499 * @param string $name
1500 * @return string
1502 protected function getFilterIdentifier($name)
1504 if (array_key_exists($name, $this->filters)) {
1505 return $name;
1508 foreach (array_keys($this->filters) as $test) {
1509 if (preg_match('/' . preg_quote($name) . '$/i', $test)) {
1510 return $test;
1514 return false;
1518 * Is the service manager component v3?
1520 * This is needed until zend-validator is updated, to ensure we instantiate
1521 * the validator plugin manager properly.
1523 * @return bool
1525 private function isServiceManagerV3()
1527 $r = new ReflectionClass(ServiceManager::class);
1528 return ! $r->hasProperty('invokableClasses');