Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 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: sfValidatorFile.class.php 32836 2011-07-27 07:15:58Z fabien $
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
}