1: <?php
2: 3: 4: 5: 6: 7:
8:
9: namespace Wei\Validator;
10:
11: 12: 13: 14: 15:
16: class File extends BaseValidator
17: {
18: protected $notFoundMessage = '%name% is not found or not readable';
19:
20: protected $maxSizeMessage = '%name% is too large(%sizeString%), allowed maximum size is %maxSizeString%';
21:
22: protected $minSizeMessage = '%name% is too small(%sizeString%), expected minimum size is %minSizeString%';
23:
24: protected $extsMessage = '%name% extension(%ext%) is not allowed, allowed extension: %exts%';
25:
26: protected $excludeExtsMessage = '%name% extension(%ext%) is not allowed, not allowed extension: %excludeExts%';
27:
28: protected $mimeTypeNotDetectedMessage = '%name% mime type could not be detected';
29:
30: protected $mimeTypesMessage = '%name% mime type "%mimeType%" is not allowed';
31:
32: protected $excludeMimeTypesMessage = '%name% mime type "%mimeType%" is not allowed';
33:
34: protected $negativeMessage = '%name% must be a non-existing file';
35:
36: 37: 38: 39: 40:
41: protected $file;
42:
43: 44: 45: 46: 47: 48:
49: protected $originFile;
50:
51: 52: 53: 54: 55:
56: protected $size;
57:
58: 59: 60: 61: 62:
63: protected $sizeString;
64:
65: 66: 67: 68: 69:
70: protected $maxSize = 0;
71:
72: 73: 74: 75: 76:
77: protected $maxSizeString;
78:
79: 80: 81: 82: 83:
84: protected $minSize = 0;
85:
86: 87: 88: 89: 90:
91: protected $minSizeString;
92:
93: 94: 95: 96: 97:
98: protected $ext;
99:
100: 101: 102: 103: 104:
105: protected $exts = array();
106:
107: 108: 109: 110: 111:
112: protected $excludeExts = array();
113:
114: 115: 116: 117: 118:
119: protected $mimeType;
120:
121: 122: 123: 124: 125:
126: protected $mimeTypes = array();
127:
128: 129: 130: 131: 132:
133: protected $excludeMimeTypes = array();
134:
135: 136: 137: 138: 139:
140: protected $units = array('B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
141:
142: 143: 144: 145: 146: 147:
148: protected $magicFile;
149:
150: 151: 152:
153: public function __invoke($input, $options = array())
154: {
155: $options && $this->storeOption($options);
156:
157: return $this->isValid($input);
158: }
159:
160: 161: 162:
163: protected function doValidate($input)
164: {
165: switch ( true ) {
166: case is_string($input) :
167: $file = $originFile = $input;
168: break;
169:
170:
171: case is_array($input) :
172: if (!isset($input['tmp_name']) || !isset($input['name'])) {
173: $this->addError('notFound');
174: return false;
175: }
176:
177: $file = $input['tmp_name'];
178: $originFile = $input['name'];
179: break;
180:
181: case $input instanceof \SplFileInfo:
182: $file = $originFile = $input->getPathname();
183: break;
184:
185: default:
186: $this->addError('notString');
187: return false;
188: }
189:
190: $this->originFile = $originFile;
191: $this->file = $file = stream_resolve_include_path($file);
192: if (!$file || !is_file($file)) {
193: $this->addError('notFound');
194: return false;
195: }
196:
197:
198: if ($this->exts || $this->excludeExts) {
199: $ext = $this->getExt();
200: }
201:
202: if ($this->excludeExts && in_array($ext, $this->excludeExts)) {
203: $this->addError('excludeExts');
204: }
205:
206: if ($this->exts && !in_array($ext, $this->exts)) {
207: $this->addError('exts');
208: }
209:
210:
211: if ($this->maxSize || $this->minSize) {
212: $this->size = filesize($file);
213: $this->sizeString = $this->fromBytes($this->size);
214: }
215:
216: if ($this->maxSize && $this->maxSize <= $this->size) {
217: $this->addError('maxSize');
218: }
219:
220: if ($this->minSize && $this->minSize > $this->size) {
221: $this->addError('minSize');
222: }
223:
224:
225: if ($this->mimeTypes || $this->excludeMimeTypes) {
226: $mimeType = $this->getMimeType();
227: if (!$mimeType) {
228: $this->addError('mimeTypeNotDetected');
229: return false;
230: }
231: }
232:
233: if ($this->mimeTypes && !$this->inMimeType($mimeType, $this->mimeTypes)) {
234: $this->addError('mimeTypes');
235: }
236:
237: if ($this->excludeMimeTypes && $this->inMimeType($mimeType, $this->excludeMimeTypes)) {
238: $this->addError('excludeMimeTypes');
239: }
240:
241: return !$this->errors;
242: }
243:
244: 245: 246: 247: 248: 249: 250: 251: 252:
253: public function inMimeType($findMe, $mimeTypes)
254: {
255: foreach ($mimeTypes as $mimeType) {
256: if ($mimeType == $findMe) {
257: return true;
258: }
259:
260: $type = strstr($mimeType, '/*', true);
261: if ($type && $type === strstr($findMe, '/', true)) {
262: return true;
263: }
264: }
265: return false;
266: }
267:
268: 269: 270: 271: 272: 273:
274: public function setExts($exts)
275: {
276: $this->exts = $this->convertToArray($exts);
277: return $this;
278: }
279:
280: 281: 282: 283: 284: 285:
286: public function setExcludeExts($exts)
287: {
288: $this->excludeExts = $this->convertToArray($exts);
289: return $this;
290: }
291:
292: 293: 294: 295: 296: 297:
298: protected function setMaxSize($maxSize)
299: {
300: $this->maxSize = $this->toBytes($maxSize);
301: $this->maxSizeString = $this->fromBytes($this->maxSize);
302: return $this;
303: }
304:
305: 306: 307: 308: 309: 310:
311: protected function setMinSize($minSize)
312: {
313: $this->minSize = $this->toBytes($minSize);
314: $this->minSizeString = $this->fromBytes($this->minSize);
315: return $this;
316: }
317:
318: 319: 320: 321: 322: 323:
324: public function toBytes($size)
325: {
326: if (is_numeric($size)) {
327: return (int) $size;
328: }
329:
330: $unit = strtoupper(substr($size, -2));
331:
332: $value = substr($size, 0, -1);
333: if (!is_numeric($value)) {
334: $value = substr($value, 0, -1);
335: }
336:
337: $exponent = array_search($unit, $this->units);
338: return (int)($value * pow(1024, $exponent));
339: }
340:
341: 342: 343: 344: 345: 346:
347: public function fromBytes($bytes)
348: {
349: for ($i=0; $bytes >= 1024 && $i < 8; $i++) {
350: $bytes /= 1024;
351: }
352: return round($bytes, 2) . $this->units[$i];
353: }
354:
355: 356: 357: 358: 359: 360:
361: public function setMimeTypes($mimeTypes)
362: {
363: $this->mimeTypes = $this->convertToArray($mimeTypes);
364: return $this;
365: }
366:
367: 368: 369: 370: 371: 372:
373: public function setExcludeMimeTypes($excludeMimeTypes)
374: {
375: $this->excludeMimeTypes = $this->convertToArray($excludeMimeTypes);
376: return $this;
377: }
378:
379: 380: 381: 382: 383: 384: 385:
386: protected function convertToArray($var)
387: {
388: if (is_string($var)) {
389: return explode(',', $var);
390: } elseif (is_array($var)) {
391: return $var;
392: } else {
393: throw new \InvalidArgumentException(sprintf(
394: 'Expected argument of type string or array, "%s" given',
395: is_object($var) ? get_class($var) : gettype($var)
396: ));
397: }
398: }
399:
400: 401: 402: 403: 404: 405:
406: public function getMimeType()
407: {
408: if (!$this->mimeType) {
409: $finfo = finfo_open(FILEINFO_MIME_TYPE, $this->magicFile);
410: if (!$finfo) {
411: throw new \UnexpectedValueException('Failed to open fileinfo database');
412: }
413: $this->mimeType = finfo_file($finfo, $this->file);
414: }
415: return $this->mimeType;
416: }
417:
418: 419: 420: 421: 422:
423: public function getExt()
424: {
425: if (is_null($this->ext) && $this->originFile) {
426: $file = basename($this->originFile);
427:
428: if (false !== $pos = strrpos($file, '.')) {
429: $this->ext = strtolower(substr($file, $pos + 1));
430: } else {
431: $this->ext = '';
432: }
433: }
434: return $this->ext;
435: }
436: }
437: