PageRenderTime 107ms CodeModel.GetById 23ms RepoModel.GetById 1ms app.codeStats 0ms

/class.xmimetypes.inc

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