Development

/branches/1.4/lib/validator/sfValidatorFile.class.php

You must first sign up to be able to contribute.

root/branches/1.4/lib/validator/sfValidatorFile.class.php

Revision 32836, 9.0 kB (checked in by fabien, 3 years ago)

[1.4] fixed guessFromFileBinary in class sfvalidatorFile does not support MIME type including dot (closes #9871)

  • Property svn:mime-type set to text/x-php
  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1 <?php
2
3 /*
4  * This file is part of the symfony package.
5  * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
6  *
7  * For the full copyright and license information, please view the LICENSE
8  * file that was distributed with this source code.
9  */
10
11 /**
12  * sfValidatorFile validates an uploaded file.
13  *
14  * @package    symfony
15  * @subpackage validator
16  * @author     Fabien Potencier <fabien.potencier@symfony-project.com>
17  * @version    SVN: $Id$
18  */
19 class sfValidatorFile extends sfValidatorBase
20 {
21   /**
22    * Configures the current validator.
23    *
24    * Available options:
25    *
26    *  * max_size:             The maximum file size in bytes (cannot exceed upload_max_filesize in php.ini)
27    *  * mime_types:           Allowed mime types array or category (available categories: web_images)
28    *  * mime_type_guessers:   An array of mime type guesser PHP callables (must return the mime type or null)
29    *  * mime_categories:      An array of mime type categories (web_images is defined by default)
30    *  * path:                 The path where to save the file - as used by the sfValidatedFile class (optional)
31    *  * validated_file_class: Name of the class that manages the cleaned uploaded file (optional)
32    *
33    * There are 3 built-in mime type guessers:
34    *
35    *  * guessFromFileinfo:        Uses the finfo_open() function (from the Fileinfo PECL extension)
36    *  * guessFromMimeContentType: Uses the mime_content_type() function (deprecated)
37    *  * guessFromFileBinary:      Uses the file binary (only works on *nix system)
38    *
39    * Available error codes:
40    *
41    *  * max_size
42    *  * mime_types
43    *  * partial
44    *  * no_tmp_dir
45    *  * cant_write
46    *  * extension
47    *
48    * @param array $options   An array of options
49    * @param array $messages  An array of error messages
50    *
51    * @see sfValidatorBase
52    */
53   protected function configure($options = array(), $messages = array())
54   {
55     if (!ini_get('file_uploads'))
56     {
57       throw new LogicException(sprintf('Unable to use a file validator as "file_uploads" is disabled in your php.ini file (%s)', get_cfg_var('cfg_file_path')));
58     }
59
60     $this->addOption('max_size');
61     $this->addOption('mime_types');
62     $this->addOption('mime_type_guessers', array(
63       array($this, 'guessFromFileinfo'),
64       array($this, 'guessFromMimeContentType'),
65       array($this, 'guessFromFileBinary'),
66     ));
67     $this->addOption('mime_categories', array(
68       'web_images' => array(
69         'image/jpeg',
70         'image/pjpeg',
71         'image/png',
72         'image/x-png',
73         'image/gif',
74     )));
75     $this->addOption('validated_file_class', 'sfValidatedFile');
76     $this->addOption('path', null);
77
78     $this->addMessage('max_size', 'File is too large (maximum is %max_size% bytes).');
79     $this->addMessage('mime_types', 'Invalid mime type (%mime_type%).');
80     $this->addMessage('partial', 'The uploaded file was only partially uploaded.');
81     $this->addMessage('no_tmp_dir', 'Missing a temporary folder.');
82     $this->addMessage('cant_write', 'Failed to write file to disk.');
83     $this->addMessage('extension', 'File upload stopped by extension.');
84   }
85
86   /**
87    * This validator always returns a sfValidatedFile object.
88    *
89    * The input value must be an array with the following keys:
90    *
91    *  * tmp_name: The absolute temporary path to the file
92    *  * name:     The original file name (optional)
93    *  * type:     The file content type (optional)
94    *  * error:    The error code (optional)
95    *  * size:     The file size in bytes (optional)
96    *
97    * @see sfValidatorBase
98    */
99   protected function doClean($value)
100   {
101     if (!is_array($value) || !isset($value['tmp_name']))
102     {
103       throw new sfValidatorError($this, 'invalid', array('value' => (string) $value));
104     }
105
106     if (!isset($value['name']))
107     {
108       $value['name'] = '';
109     }
110
111     if (!isset($value['error']))
112     {
113       $value['error'] = UPLOAD_ERR_OK;
114     }
115
116     if (!isset($value['size']))
117     {
118       $value['size'] = filesize($value['tmp_name']);
119     }
120
121     if (!isset($value['type']))
122     {
123       $value['type'] = 'application/octet-stream';
124     }
125
126     switch ($value['error'])
127     {
128       case UPLOAD_ERR_INI_SIZE:
129         $max = ini_get('upload_max_filesize');
130         if ($this->getOption('max_size'))
131         {
132           $max = min($max, $this->getOption('max_size'));
133         }
134         throw new sfValidatorError($this, 'max_size', array('max_size' => $max, 'size' => (int) $value['size']));
135       case UPLOAD_ERR_FORM_SIZE:
136         throw new sfValidatorError($this, 'max_size', array('max_size' => 0, 'size' => (int) $value['size']));
137       case UPLOAD_ERR_PARTIAL:
138         throw new sfValidatorError($this, 'partial');
139       case UPLOAD_ERR_NO_TMP_DIR:
140         throw new sfValidatorError($this, 'no_tmp_dir');
141       case UPLOAD_ERR_CANT_WRITE:
142         throw new sfValidatorError($this, 'cant_write');
143       case UPLOAD_ERR_EXTENSION:
144         throw new sfValidatorError($this, 'extension');
145     }
146
147     // check file size
148     if ($this->hasOption('max_size') && $this->getOption('max_size') < (int) $value['size'])
149     {
150       throw new sfValidatorError($this, 'max_size', array('max_size' => $this->getOption('max_size'), 'size' => (int) $value['size']));
151     }
152
153     $mimeType = $this->getMimeType((string) $value['tmp_name'], (string) $value['type']);
154
155     // check mime type
156     if ($this->hasOption('mime_types'))
157     {
158       $mimeTypes = is_array($this->getOption('mime_types')) ? $this->getOption('mime_types') : $this->getMimeTypesFromCategory($this->getOption('mime_types'));
159       if (!in_array($mimeType, array_map('strtolower', $mimeTypes)))
160       {
161         throw new sfValidatorError($this, 'mime_types', array('mime_types' => $mimeTypes, 'mime_type' => $mimeType));
162       }
163     }
164
165     $class = $this->getOption('validated_file_class');
166
167     return new $class($value['name'], $mimeType, $value['tmp_name'], $value['size'], $this->getOption('path'));
168   }
169
170   /**
171    * Returns the mime type of a file.
172    *
173    * This methods call each mime_type_guessers option callables to
174    * guess the mime type.
175    *
176    * This method always returns a lower-cased string as mime types are case-insensitive
177    * as per the RFC 2616 (http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7).
178    *
179    * @param  string $file      The absolute path of a file
180    * @param  string $fallback  The default mime type to return if not guessable
181    *
182    * @return string The mime type of the file (fallback is returned if not guessable)
183    */
184   protected function getMimeType($file, $fallback)
185   {
186     foreach ($this->getOption('mime_type_guessers') as $method)
187     {
188       $type = call_user_func($method, $file);
189
190       if (null !== $type && $type !== false)
191       {
192         return strtolower($type);
193       }
194     }
195
196     return strtolower($fallback);
197   }
198
199   /**
200    * Guess the file mime type with PECL Fileinfo extension
201    *
202    * @param  string $file  The absolute path of a file
203    *
204    * @return string The mime type of the file (null if not guessable)
205    */
206   protected function guessFromFileinfo($file)
207   {
208     if (!function_exists('finfo_open') || !is_readable($file))
209     {
210       return null;
211     }
212
213     if (!$finfo = new finfo(FILEINFO_MIME))
214     {
215       return null;
216     }
217
218     $type = $finfo->file($file);
219
220     // remove charset (added as of PHP 5.3)
221     if (false !== $pos = strpos($type, ';'))
222     {
223       $type = substr($type, 0, $pos);
224     }
225
226     return $type;
227   }
228
229   /**
230    * Guess the file mime type with mime_content_type function (deprecated)
231    *
232    * @param  string $file  The absolute path of a file
233    *
234    * @return string The mime type of the file (null if not guessable)
235    */
236   protected function guessFromMimeContentType($file)
237   {
238     if (!function_exists('mime_content_type') || !is_readable($file))
239     {
240       return null;
241     }
242
243     return mime_content_type($file);
244   }
245
246   /**
247    * Guess the file mime type with the file binary (only available on *nix)
248    *
249    * @param  string $file  The absolute path of a file
250    *
251    * @return string The mime type of the file (null if not guessable)
252    */
253   protected function guessFromFileBinary($file)
254   {
255     ob_start();
256     //need to use --mime instead of -i. see #6641
257     passthru(sprintf('file -b --mime %s 2>/dev/null', escapeshellarg($file)), $return);
258     if ($return > 0)
259     {
260       ob_end_clean();
261
262       return null;
263     }
264     $type = trim(ob_get_clean());
265
266     if (!preg_match('#^([a-z0-9\-]+/[a-z0-9\-.]+)#i', $type, $match))
267     {
268       // it's not a type, but an error message
269       return null;
270     }
271
272     return $match[1];
273   }
274
275   protected function getMimeTypesFromCategory($category)
276   {
277     $categories = $this->getOption('mime_categories');
278
279     if (!isset($categories[$category]))
280     {
281       throw new InvalidArgumentException(sprintf('Invalid mime type category "%s".', $category));
282     }
283
284     return $categories[$category];
285   }
286
287   /**
288    * @see sfValidatorBase
289    */
290   protected function isEmpty($value)
291   {
292     // empty if the value is not an array
293     // or if the value comes from PHP with an error of UPLOAD_ERR_NO_FILE
294     return
295       (!is_array($value))
296         ||
297       (is_array($value) && isset($value['error']) && UPLOAD_ERR_NO_FILE === $value['error']);
298   }
299 }
300
Note: See TracBrowser for help on using the browser.