Autodoc
  • Namespace
  • Class
  • Tree

Namespaces

  • BlueTihi
    • Context
  • Brickrouge
    • Element
      • Nodes
    • Renderer
    • Widget
  • ICanBoogie
    • ActiveRecord
    • AutoConfig
    • CLDR
    • Composer
    • Core
    • Event
    • Exception
    • HTTP
      • Dispatcher
      • Request
    • I18n
      • Translator
    • Mailer
    • Modules
      • Taxonomy
        • Support
      • Thumbnailer
        • Versions
    • Object
    • Operation
      • Dispatcher
    • Prototype
    • Routes
    • Routing
      • Dispatcher
    • Session
  • Icybee
    • ActiveRecord
      • Model
    • ConfigOperation
    • Document
    • EditBlock
    • Element
      • ActionbarContextual
      • ActionbarSearch
      • ActionbarToolbar
    • FormBlock
    • Installer
    • ManageBlock
    • Modules
      • Articles
      • Cache
        • Collection
        • ManageBlock
      • Comments
        • ManageBlock
      • Contents
        • ManageBlock
      • Dashboard
      • Editor
        • Collection
      • Files
        • File
        • ManageBlock
      • Forms
        • Form
        • ManageBlock
      • I18n
      • Images
        • ManageBlock
      • Members
      • Modules
        • ManageBlock
      • Nodes
        • ManageBlock
        • Module
      • Pages
        • BreadcrumbElement
        • LanguagesElement
        • ManageBlock
        • NavigationBranchElement
        • NavigationElement
        • Page
        • PageController
      • Registry
      • Search
      • Seo
      • Sites
        • ManageBlock
      • Taxonomy
        • Terms
          • ManageBlock
        • Vocabulary
          • ManageBlock
      • Users
        • ManageBlock
        • NonceLogin
        • Roles
      • Views
        • ActiveRecordProvider
        • Collection
        • View
    • Operation
      • ActiveRecord
      • Constructor
      • Module
      • Widget
    • Rendering
  • None
  • Patron
  • PHP

Classes

  • CacheControlHeader
  • CallableDispatcher
  • ContentDispositionHeader
  • ContentTypeHeader
  • DateHeader
  • Dispatcher
  • File
  • FileList
  • Header
  • HeaderParameter
  • Headers
  • Helpers
  • RedirectResponse
  • Request
  • Response
  • WeightedDispatcher

Interfaces

  • IDispatcher

Exceptions

  • ForceRedirect
  • HTTPError
  • MethodNotSupported
  • NotFound
  • ServiceUnavailable
  • StatusCodeNotValid

Functions

  • dispatch
  • get_dispatcher
  • get_initial_request
  1 <?php
  2 
  3 /*
  4  * This file is part of the ICanBoogie package.
  5  *
  6  * (c) Olivier Laviale <olivier.laviale@gmail.com>
  7  *
  8  * For the full copyright and license information, please view the LICENSE
  9  * file that was distributed with this source code.
 10  */
 11 
 12 namespace ICanBoogie\HTTP;
 13 
 14 use ICanBoogie\ToArray;
 15 
 16 /**
 17  * Representation of a POST file.
 18  *
 19  * @property-read string $name Name of the file.
 20  * @property-read string $type MIME type of the file.
 21  * @property-read string $size Size of the file.
 22  * @property-read string $error Error code, one of `UPLOAD_ERR_*`.
 23  * @property-read string $error_message A formatted message representing the error.
 24  * @property-read string $pathname Pathname of the file.
 25  * @property-read string $extension The extension of the file. If any, the dot is included e.g.
 26  * ".zip".
 27  * @property-read string $unsuffixed_name The name of the file without its extension.
 28  * @property-read bool $is_uploaded `true` if the file is uploaded, `false` otherwise.
 29  * @property-read bool $is_valid `true` if the file is valid, `false` otherwise.
 30  * See: {@link is_valid()}.
 31  */
 32 class File implements ToArray
 33 {
 34     use \ICanBoogie\PrototypeTrait;
 35 
 36     static protected $types = [
 37 
 38         '.doc'  => 'application/msword',
 39         '.docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
 40         '.gif'  => 'image/gif',
 41         '.jpg'  => 'image/jpeg',
 42         '.jpeg' => 'image/jpeg',
 43         '.js'   => 'application/javascript',
 44         '.mp3'  => 'audio/mpeg',
 45         '.odt'  => 'application/vnd.oasis.opendocument.text',
 46         '.pdf'  => 'application/pdf',
 47         '.php'  => 'application/x-php',
 48         '.png'  => 'image/png',
 49         '.psd'  => 'application/psd',
 50         '.rar'  => 'application/rar',
 51         '.txt'  => 'text/plain',
 52         '.zip'  => 'application/zip',
 53         '.xls'  => 'application/vnd.ms-excel',
 54         '.xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
 55 
 56     ];
 57 
 58     static protected $types_alias = [
 59 
 60         'text/x-php' => 'application/x-php'
 61 
 62     ];
 63 
 64     static public function from($properties_or_name)
 65     {
 66         $properties = [];
 67 
 68         if (is_string($properties_or_name))
 69         {
 70             $properties = isset($_FILES[$properties_or_name])
 71             ? $_FILES[$properties_or_name]
 72             : [ 'name' => $properties_or_name];
 73         }
 74         else if (is_array($properties_or_name))
 75         {
 76             $properties = $properties_or_name;
 77         }
 78 
 79         return new static($properties);
 80     }
 81 
 82     /**
 83      * Resolve the MIME type of a file.
 84      *
 85      * @param string $pathname Pathname to the file.
 86      * @param string $extension The variable passed by reference recieves the extension
 87      * of the file.
 88      *
 89      * @return string The MIME type of the file, or 'application/octet-stream' if it could not
 90      * be determined.
 91      */
 92     static public function resolve_type($pathname, &$extension=null)
 93     {
 94         $extension = '.' . strtolower(pathinfo($pathname, PATHINFO_EXTENSION));
 95 
 96         if (file_exists($pathname) && extension_loaded('fileinfo'))
 97         {
 98             $fi = new \finfo(FILEINFO_MIME_TYPE);
 99             $type = $fi->file($pathname);
100 
101             if ($type)
102             {
103                 return isset(self::$types_alias[$type]) ? self::$types_alias[$type] : $type;
104             }
105         }
106 
107         if (isset(self::$types[$extension]))
108         {
109             return self::$types[$extension];
110         }
111 
112         return 'application/octet-stream';
113     }
114 
115     /**
116      * Format a message.
117      *
118      * @return \ICanBoogie\I18n\FormattedString|\ICanBoogie\FormattedString|string
119      */
120     static private function format($format, array $args=[], array $options=[])
121     {
122         if (class_exists('ICanBoogie\I18n\FormattedString', true))
123         {
124             return new \ICanBoogie\I18n\FormattedString($format, $args, $options);
125         }
126 
127         if (class_exists('ICanBoogie\FormattedString', true))
128         {
129             return new \ICanBoogie\FormattedString($format, $args, $options);
130         }
131 
132         return \ICanBoogie\format($format, $args, $options);
133     }
134 
135     /*
136      * Instance
137      */
138 
139     protected $name;
140 
141     /**
142      * Returns the name of the file.
143      *
144      * @return string
145      */
146     protected function get_name()
147     {
148         return $this->name;
149     }
150 
151     /**
152      * Returns the name of the file, without its extension.
153      *
154      * @return string
155      */
156     protected function get_unsuffixed_name()
157     {
158         return $this->name ? basename($this->name, $this->extension) : null;
159     }
160 
161     protected $type;
162 
163     /**
164      * Returns the type of the file.
165      *
166      * If the {@link $type} property was not defined during construct, the type
167      * is guessed from the name or the pathname of the file.
168      *
169      * @return string|null The MIME type of the file, or `null` if it cannot be determined.
170      */
171     protected function get_type()
172     {
173         if (!empty($this->type))
174         {
175             return $this->type;
176         }
177 
178         if (!$this->pathname && !$this->tmp_name)
179         {
180             return;
181         }
182 
183         return self::resolve_type($this->pathname ?: $this->tmp_name);
184     }
185 
186     protected $size;
187 
188     /**
189      * Returns the size of the file.
190      *
191      * If the {@link $size} property was not defined during construct, the size
192      * is guessed using the pathname of the file. If the pathname is not available the method
193      * returns `null`.
194      *
195      * @return int|null The size of the file or `null` if it cannot be determined.
196      */
197     protected function get_size()
198     {
199         if (!empty($this->size))
200         {
201             return $this->size;
202         }
203 
204         if ($this->pathname)
205         {
206             return filesize($this->pathname);
207         }
208     }
209 
210     protected $tmp_name;
211 
212     protected $error;
213 
214     /**
215      * Check if the file is valid.
216      *
217      * A file is considered valid if it has no error code, if it has a size,
218      * if it has either a temporary name or a pathname and that the file actually exists.
219      *
220      * @return boolean `true` if the file is valid, `false` otherwise.
221      */
222     protected function get_is_valid()
223     {
224         return !$this->error
225         && $this->size
226         && ($this->tmp_name || ($this->pathname && file_exists($this->pathname)));
227     }
228 
229     protected $pathname;
230 
231     /**
232      * Return the pathname of the file.
233      *
234      * Note: If the {@link $pathname} property is empty, the {@link $tmp_name} property
235      * is returned.
236      *
237      * @return string
238      */
239     protected function get_pathname()
240     {
241         return $this->pathname ?: $this->tmp_name;
242     }
243 
244     protected function __construct(array $properties)
245     {
246         static $initial_properties = [ 'name', 'type', 'size', 'tmp_name', 'error', 'pathname' ];
247 
248         foreach ($properties as $property => $value)
249         {
250             if (!in_array($property, $initial_properties))
251             {
252                 continue;
253             }
254 
255             $this->$property = $value;
256         }
257 
258         if (!$this->name && $this->pathname)
259         {
260             $this->name = basename($this->pathname);
261         }
262 
263         if (empty($this->type))
264         {
265             unset($this->type);
266         }
267 
268         if (empty($this->size))
269         {
270             unset($this->size);
271         }
272     }
273 
274     /**
275      * Return an array representation of the instance.
276      *
277      * The following properties are exported:
278      *
279      * - {@link $name}
280      * - {@link $unsuffixed_name}
281      * - {@link $extension}
282      * - {@link $type}
283      * - {@link $size}
284      * - {@link $pathname}
285      * - {@link $error}
286      * - {@link $error_message}
287      *
288      * @return array
289      */
290     public function to_array()
291     {
292         $error_message = $this->error_message;
293 
294         if ($error_message !== null)
295         {
296             $error_message = (string) $error_message;
297         }
298 
299         return [
300 
301             'name' => $this->name,
302             'unsuffixed_name' => $this->unsuffixed_name,
303             'extension' => $this->extension,
304             'type' => $this->type,
305             'size' => $this->size,
306             'pathname' => $this->pathname,
307             'error' => $this->error,
308             'error_message' => $error_message
309 
310         ];
311     }
312 
313     /**
314      * Returns the error code.
315      *
316      * @return string
317      */
318     protected function get_error()
319     {
320         return $this->error;
321     }
322 
323     /**
324      * Returns the message associated with the error.
325      *
326      * @return \ICanBoogie\I18n\FormattedString|\ICanBoogie\FormattedString|string|null
327      */
328     protected function get_error_message()
329     {
330         switch ($this->error)
331         {
332             case UPLOAD_ERR_OK:
333 
334                 return;
335 
336             case UPLOAD_ERR_INI_SIZE:
337 
338                 return $this->format("Maximum file size is :size Mb", [ ':size' => (int) ini_get('upload_max_filesize') ]);
339 
340             case UPLOAD_ERR_FORM_SIZE:
341 
342                 return $this->format("Maximum file size is :size Mb", [ ':size' => 'MAX_FILE_SIZE' ]);
343 
344             case UPLOAD_ERR_PARTIAL:
345 
346                 return $this->format("The uploaded file was only partially uploaded.");
347 
348             case UPLOAD_ERR_NO_FILE:
349 
350                 return $this->format("No file was uploaded.");
351 
352             case UPLOAD_ERR_NO_TMP_DIR:
353 
354                 return $this->format("Missing a temporary folder.");
355 
356             case UPLOAD_ERR_CANT_WRITE:
357 
358                 return $this->format("Failed to write file to disk.");
359 
360             case UPLOAD_ERR_EXTENSION:
361 
362                 return $this->format("A PHP extension stopped the file upload.");
363 
364             default:
365 
366                 return $this->format("An error has occured.");
367         }
368     }
369 
370     /**
371      * Returns the extension of the file, if any.
372      *
373      * Note: The extension includes the dot e.g. ".zip". The extension if always in lower case.
374      *
375      * @return string|null
376      */
377     protected function get_extension()
378     {
379         $extension = pathinfo($this->name, PATHINFO_EXTENSION);
380 
381         if (!$extension)
382         {
383             return;
384         }
385 
386         return '.' . strtolower($extension);
387     }
388 
389     /**
390      * Check if a file is uploaded.
391      *
392      * @return boolean `true` if the file is uploaded, `false` otherwise.
393      */
394     protected function get_is_uploaded()
395     {
396         return $this->tmp_name && is_uploaded_file($this->tmp_name);
397     }
398 
399     /**
400      * Check if the file matches a MIME class, a MIME type, or a file extension.
401      *
402      * @param string|array $type The type can be a MIME class (e.g. "image"),
403      * a MIME type (e.g. "image/png"), or an extensions (e.g. ".zip"). An array can be used to
404      * check if a file matches multiple type e.g. `[ "image", ".mp3" ]`, which matches any type
405      * of image or files with the ".mp3" extension.
406      *
407      * @return boolean `true` if the file matches (or `$type` is empty), `false` otherwise.
408      */
409     public function match($type)
410     {
411         if (!$type)
412         {
413             return true;
414         }
415 
416         if (is_array($type))
417         {
418             $type_list = $type;
419 
420             foreach ($type_list as $type)
421             {
422                 if ($this->match($type))
423                 {
424                     return true;
425                 }
426             }
427 
428             return false;
429         }
430 
431         if ($type{0} === '.')
432         {
433             return $type === $this->extension;
434         }
435 
436         if (strpos($type, '/') === false)
437         {
438             return (bool) preg_match('#^' . preg_quote($type) . '/#', $this->type);
439         }
440 
441         return $type === $this->type;
442     }
443 
444     /**
445      * Move the file.
446      *
447      * @param string $destination Pathname to the destination file.
448      * @param string $overwrite If `true` the destination file is deleted before the file is move.
449      *
450      * @throws \Exception if the file failed to be moved.
451      */
452     public function move($destination, $overwrite=false)
453     {
454         if (file_exists($destination))
455         {
456             if (!$overwrite)
457             {
458                 throw new \Exception("The destination file already exists: $destination.");
459             }
460 
461             unlink($destination);
462         }
463 
464         if ($this->pathname)
465         {
466             if (!rename($this->pathname, $destination))
467             {
468                 throw new \Exception("Unable to move file to destination: $destination.");
469             }
470         }
471         else
472         {
473             if (!move_uploaded_file($this->tmp_name, $destination))
474             {
475                 throw new \Exception("Unable to move file to destination: $destination.");
476             }
477         }
478 
479         $this->pathname = $destination;
480     }
481 }
Autodoc API documentation generated by ApiGen 2.8.0