Source for file class.mimetypes.php

Documentation is available at class.mimetypes.php


1 <?php
2 // +----------------------------------------------------------------------+
3 // | MIME Types Class 0.1 - 21-Dec-2002 |
4 // +----------------------------------------------------------------------+
5 // | Author: Keyvan Minoukadeh - keyvan@k1m.com - http://www.keyvan.net |
6 // +----------------------------------------------------------------------+
7 // | PHP class for retrieving the appropriate MIME type of a |
8 // | file/extension |
9 // +----------------------------------------------------------------------+
10 // | This program is free software; you can redistribute it and/or |
11 // | modify it under the terms of the GNU General Public License |
12 // | as published by the Free Software Foundation; either version 2 |
13 // | of the License, or (at your option) any later version. |
14 // | |
15 // | This program is distributed in the hope that it will be useful, |
16 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 // | GNU General Public License for more details. |
19 // +----------------------------------------------------------------------+
20
21 /**
22 * MIME Types class
23 *
24 * This class allows you to:
25 * - Retrieve the appropriate MIME type of a file (based on it's extension, or by utilising
26 * the file command to guess it based on the file contents).
27 * - Retrieve extension(s) associated with a MIME type.
28 * - Load MIME types and extensions from a mime.types file.
29 *
30 * Example:
31 * $mime =& new Mime_Types('/usr/local/apache/conf/mime.types');
32 * echo $mime->get_type('pdf'); // application/pdf
33 * echo $mime->get_extension('text/vnd.wap.wmlscript'); // wmls
34 *
35 * See test_Mime_Types.php file for more examples.
36 *
37 * TODO:
38 * - method to write MIME types to file.
39 * - get_file_type(): possibly preserving the parameters returned by the file command
40 * (e.g. text/plain; charset=us-ascii)
41 *
42 * @author Keyvan Minoukadeh <keyvan@k1m.com>
43 * @version 0.1
44 */
45 class Mime_Types
46 {
47 /**
48 * MIME Types
49 * Initially we start with the more popular ones.
50 * ["txt"] => "text/plain",
51 * ["gif"] => "image/gif",
52 * ["jpg"] => "image/jpeg",
53 * ["html"] => "text/html",
54 * ["htm"] => "text/html"
55 * @var array
56 * @access private
57 */
58 var $mime_types = array(
59 'txt' => 'text/plain',
60 'gif' => 'image/gif',
61 'jpg' => 'image/jpeg',
62 'html' => 'text/html',
63 'htm' => 'text/html');
64
65 /**
66 * Path to file command - empty string disables the use of the file command
67 * @var string
68 */
69 var $file_cmd = '';
70 // var $file_cmd = '/usr/bin/file';
71
72 /**
73 * File options, used with the file command
74 * Example:
75 * ['i'] => null // this option asks file to produce a MIME type if it can
76 * ['b'] => null // this option tells file to be brief
77 * will result in 'file -i -b test_file'
78 * @var array
79 */
80 var $file_options = array('b'=>null, 'i'=>null);
81
82 /**
83 * Constructor
84 * optional parameter can be either a string containing the path to the
85 * mime.types files, or an associative array holding the extension
86 * as key and the MIME type as value.
87 * Example:
88 * $mime =& new Mime_Types('/usr/local/apache/conf/mime.types');
89 * or
90 * $mime =& new Mime_Types(array(
91 * 'application/pdf' => 'pdf',
92 * 'application/postscript' => array('ai','eps')
93 * ));
94 * @param mixed $mime_types
95 */
96 function Mime_Types($mime_types=null)
97 {
98 if (is_string($mime_types)) {
99 $this->load_file($mime_types);
100 } elseif (is_array($mime_types)) {
101 $this->set($mime_types);
102 }
103 }
104
105 /**
106 * Scan - goes through all MIME types passing the extension and type to the callback function.
107 * The types will be sent in alphabetical order.
108 * If a type has multiple extensions, each extension will be passed seperately (not as an array).
109 *
110 * The callback function can be a method from another object (eg. array(&$my_obj, 'my_method')).
111 * The callback function should accept 3 arguments:
112 * 1- A reference to the Mime_Types object (&$mime)
113 * 2- An array holding extension and type, array keys:
114 * [0]=>ext, [1]=>type
115 * 3- An optional parameter which can be used for whatever your function wants :),
116 * even though you might not have a use for this parameter, you need to define
117 * your function to accept it. (Note: you can have this parameter be passed by reference)
118 * The callback function should return a boolean, a value of 'true' will tell scan() you want
119 * it to continue with the rest of the types, 'false' will tell scan() to stop calling
120 * your callback function.
121 *
122 * @param mixed $callback function name, or array holding an object and the method to call.
123 * @param mixed $param passed as the 3rd argument to $callback
124 */
125 function scan($callback, &$param)
126 {
127 if (is_array($callback)) $method =& $callback[1];
128 $mime_types = $this->mime_types;
129 asort($mime_types);
130 foreach ($mime_types as $ext => $type) {
131 $ext_type = array($ext, $type);
132 if (isset($method)) {
133 $res = $callback[0]->$method($this, $ext_type, $param);
134 } else {
135 $res = $callback($this, $ext_type, $param);
136 }
137 if (!$res) return;
138 }
139 }
140
141 /**
142 * Get file type - returns MIME type by trying to guess it using the file command.
143 * Optional second parameter should be a boolean. If true (default), get_file_type() will
144 * try to guess the MIME type based on the file extension if the file command fails to find
145 * a match.
146 * Example:
147 * echo $mime->get_file_type('/path/to/my_file', false);
148 * or
149 * echo $mime->get_file_type('/path/to/my_file.gif');
150 * @param string $file
151 * @param bool $use_ext default: true
152 * @return string false if unable to find suitable match
153 */
154 function get_file_type($file, $use_ext=true)
155 {
156 $file = trim($file);
157 if ($file == '') return false;
158 $type = false;
159 $result = false;
160 if ($this->file_cmd && is_readable($file) && is_executable($this->file_cmd)) {
161 $cmd = $this->file_cmd;
162 foreach ($this->file_options as $option_key => $option_val) {
163 $cmd .= ' -'.$option_key;
164 if (isset($option_val)) $cmd .= ' '.escapeshellarg($option_val);
165 }
166 $cmd .= ' '.escapeshellarg($file);
167 $result = @exec($cmd);
168 if ($result) {
169 $result = strtolower($result);
170 $pattern = '[a-z0-9.+_-]';
171 if (preg_match('!(('.$pattern.'+)/'.$pattern.'+)!', $result, $match)) {
172 if (in_array($match[2], array('application','audio','image','message',
173 'multipart','text','video','chemical','model')) ||
174 (substr($match[2], 0, 2) == 'x-')) {
175 $type = $match[1];
176 }
177 }
178 }
179 }
180 // try and get type from extension
181 if (!$type && $use_ext && strpos($file, '.')) $type = $this->get_type($file);
182 // this should be some sort of attempt to match keywords in the file command output
183 // to a MIME type, I'm not actually sure if this is a good idea, but for now, it tries
184 // to find an 'ascii' string.
185 if (!$type && $result && preg_match('/\bascii\b/', $result)) $type = 'text/plain';
186 return $type;
187 }
188
189 /**
190 * Get type - returns MIME type based on the file extension.
191 * Example:
192 * echo $mime->get_type('txt');
193 * or
194 * echo $mime->get_type('test_file.txt');
195 * both examples above will return the same result.
196 * @param string $ext
197 * @return string false if extension not found
198 */
199 function get_type($ext)
200 {
201 $ext = strtolower($ext);
202 // get position of last dot
203 $dot_pos = strrpos($ext, '.');
204 if ($dot_pos !== false) $ext = substr($ext, $dot_pos+1);
205 if (($ext != '') && isset($this->mime_types[$ext])) return $this->mime_types[$ext];
206 return false;
207 }
208
209 /**
210 * Set - set extension and MIME type
211 * Example:
212 * $mime->set('text/plain', 'txt');
213 * or
214 * $mime->set('text/html', array('html','htm'));
215 * or
216 * $mime->set('text/html', 'html htm');
217 * or
218 * $mime->set(array(
219 * 'application/pdf' => 'oda',
220 * 'application/postscript' => array('ai','eps')
221 * ));
222 * @param mixed $type either array containing type and extensions, or the type as string
223 * @param mixed $exts either array holding extensions, or string holding extensions
224 * seperated by space.
225 * @return void
226 */
227 function set($type, $exts=null)
228 {
229 if (!isset($exts)) {
230 if (is_array($type)) {
231 foreach ($type as $mime_type => $exts) {
232 $this->set($mime_type, $exts);
233 }
234 }
235 return;
236 }
237 if (!is_string($type)) return;
238 // get rid of any parameters which might be included with the MIME type
239 // e.g. text/plain; charset=iso-8859-1
240 $type = strtr(strtolower(trim($type)), ",;\t\r\n", ' ');
241 if ($sp_pos = strpos($type, ' ')) $type = substr($type, 0, $sp_pos);
242 // not bothering with an extensive check of the MIME type, just checking for slash
243 if (!strpos($type, '/')) return;
244 // loop through extensions
245 if (!is_array($exts)) $exts = explode(' ', $exts);
246 foreach ($exts as $ext) {
247 $ext = trim(str_replace('.', '', $ext));
248 if ($ext == '') continue;
249 $this->mime_types[strtolower($ext)] = $type;
250 }
251 }
252
253 /**
254 * Has extension - returns true if extension $ext exists, false otherwise
255 * Example:
256 * if ($mime->has_extension('pdf')) echo 'Got it!';
257 * @param string $ext
258 * @return bool
259 */
260 function has_extension($ext)
261 {
262 return (isset($this->mime_types[strtolower($ext)]));
263 }
264
265 /**
266 * Has type - returns true if type $type exists, false otherwise
267 * Example:
268 * if ($mime->has_type('image/gif')) echo 'Got it!';
269 * @param string $type
270 * @return bool
271 */
272 function has_type($type)
273 {
274 return (in_array(strtolower($type), $this->mime_types));
275 }
276
277 /**
278 * Get extension - returns string containing a extension associated with $type
279 * Example:
280 * $ext = $mime->get_extension('application/postscript');
281 * if ($ext) echo $ext;
282 * @param string $type
283 * @return string false if $type not found
284 */
285 function get_extension($type)
286 {
287 $type = strtolower($type);
288 foreach ($this->mime_types as $ext => $m_type) {
289 if ($m_type == $type) return $ext;
290 }
291 return false;
292 }
293
294 /**
295 * Get extensions - returns array containing extension(s)
296 * Example:
297 * $exts = $mime->get_extensions('application/postscript');
298 * echo implode(', ', $exts);
299 * @param string $type
300 * @return array
301 */
302 function get_extensions($type)
303 {
304 $type = strtolower($type);
305 return (array_keys($this->mime_types, $type));
306 }
307
308 /**
309 * Remove extension
310 * Example:
311 * $mime->remove_extension('txt');
312 * or
313 * $mime->remove_extension('txt exe html');
314 * or
315 * $mime->remove_extension(array('txt', 'exe', 'html'));
316 * @param mixed $exts string holding extension(s) seperated by space, or array
317 * @return void
318 */
319 function remove_extension($exts)
320 {
321 if (!is_array($exts)) $exts = explode(' ', $exts);
322 foreach ($exts as $ext) {
323 $ext = strtolower(trim($ext));
324 if (isset($this->mime_types[$ext])) unset($this->mime_types[$ext]);
325 }
326 }
327
328 /**
329 * Remove type
330 * Example:
331 * $mime->remove_type('text/plain');
332 * or
333 * $mime->remove_type('image/*');
334 * // removes all image types
335 * or
336 * $mime->remove_type();
337 * // clears all types
338 * @param string $type if omitted, all types will be removed
339 * @return void
340 */
341 function remove_type($type=null)
342 {
343 if (!isset($type)) {
344 $this->mime_types = array();
345 return;
346 }
347 $slash_pos = strpos($type, '/');
348 if (!$slash_pos) return;
349
350 $type_info = array('last_match'=>false, 'wildcard'=>false, 'type'=>$type);
351 if (substr($type, $slash_pos) == '/*') {
352 $type_info['wildcard'] = true;
353 $type_info['type'] = substr($type, 0, $slash_pos);
354 }
355 $this->scan(array(&$this, '_remove_type_callback'), $type_info);
356 }
357
358 /**
359 * Load file - load file containing mime types.
360 * Example:
361 * $result = $mime->load_file('/usr/local/apache/conf/mime.types');
362 * echo (($result) ? 'Success!' : 'Failed');
363 * @param string $file
364 * @return bool
365 */
366 function load_file($file)
367 {
368 if (!file_exists($file) || !is_readable($file)) return false;
369 $data = file($file);
370 foreach ($data as $line) {
371 $line = trim($line);
372 if (($line == '') || ($line == '#')) continue;
373 $line = preg_split('/\s+/', $line, 2);
374 if (count($line) < 2) continue;
375 $exts = $line[1];
376 // if there's a comment on this line, remove it
377 $hash_pos = strpos($exts, '#');
378 if ($hash_pos !== false) $exts = substr($exts, 0, $hash_pos);
379 $this->set($line[0], $exts);
380 }
381 return true;
382 }
383
384 //
385 // private methods
386 //
387
388 /**
389 * Remove type callback
390 * @param object $mime
391 * @param array $ext_type
392 * @param array $type_info
393 * @return bool
394 * @access private
395 */
396 function _remove_type_callback(&$mime, $ext_type, $type_info)
397 {
398 // temporarily we'll put match to false
399 $matched = false;
400 list($ext, $type) = $ext_type;
401 if ($type_info['wildcard']) {
402 if (substr($type, 0, strpos($type, '/')) == $type_info['type']) {
403 $matched = true;
404 }
405 } elseif ($type == $type_info['type']) {
406 $matched = true;
407 }
408 if ($matched) {
409 $this->remove_extension($ext);
410 $type_info['last_match'] = true;
411 } elseif ($type_info['last_match']) {
412 // we do not need to continue if the previous type matched, but this type didn't.
413 // because all types are sorted in alphabetical order, we can be sure there will be
414 // no further successful matches.
415 return false;
416 }
417 return true;
418 }
419 }
420 ?>

Documentation generated on Thu, 9 Jun 2005 06:51:42 +0200 by phpDocumentor 1.2.3