/src/Zend/Mime/Magic.php

http://php-reader.googlecode.com/ · PHP · 196 lines · 97 code · 10 blank · 89 comment · 11 complexity · 1a36de594b5eeeeea90401de55abd2a7 MD5 · raw file

  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_Mime
  17. * @subpackage Magic
  18. * @copyright Copyright (c) 2005-2009 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Magic.php 193 2010-04-08 14:46:41Z svollbehr $
  21. */
  22. /**#@+ @ignore */
  23. require_once 'Zend/Io/FileReader.php';
  24. /**#@-*/
  25. /**
  26. * This class is used to classify the given file using some magic bytes
  27. * characteristic to a particular file type. The classification information can
  28. * be a MIME type or just text describing the file.
  29. *
  30. * This method is slower than determining the type by file suffix but on the
  31. * other hand reduces the risk of fail positives during the test.
  32. *
  33. * The magic file consists of ASCII characters defining the magic numbers for
  34. * different file types. Each row has 4 to 5 columns, empty and commented lines
  35. * (those starting with a hash character) are ignored. Columns are described
  36. * below.
  37. *
  38. * o <b>1</b> -- byte number to begin checking from. '>' indicates a dependency
  39. * upon the previous non-'>' line
  40. * o <b>2</b> -- type of data to match. Can be one of following
  41. * - <i>byte</i> (single character)
  42. * - <i>short</i> (machine-order 16-bit integer)
  43. * - <i>long</i> (machine-order 32-bit integer)
  44. * - <i>string</i> (arbitrary-length string)
  45. * - <i>date</i> (long integer date (seconds since Unix epoch/1970))
  46. * - <i>beshort</i> (big-endian 16-bit integer)
  47. * - <i>belong</i> (big-endian 32-bit integer)
  48. * - <i>bedate</i> (big-endian 32-bit integer date)
  49. * - <i>leshort</i> (little-endian 16-bit integer)
  50. * - <i>lelong</i> (little-endian 32-bit integer)
  51. * - <i>ledate</i> (little-endian 32-bit integer date)
  52. * o <b>3</b> -- contents of data to match
  53. * o <b>4</b> -- file description/MIME type if matched
  54. * o <b>5</b> -- optional MIME encoding if matched and if above was a MIME type
  55. *
  56. * @category Zend
  57. * @package Zend_Mime
  58. * @subpackage Magic
  59. * @author Sven Vollbehr <sven@vollbehr.eu>
  60. * @copyright Copyright (c) 2005-2010 Zend Technologies USA Inc. (http://www.zend.com)
  61. * @license http://framework.zend.com/license/new-bsd New BSD License
  62. * @version $Id: Magic.php 193 2010-04-08 14:46:41Z svollbehr $
  63. */
  64. final class Zend_Mime_Magic
  65. {
  66. /** @var string */
  67. private $_magic;
  68. /**
  69. * Reads the magic information from given magic file.
  70. *
  71. * @param string $filename The path to the magic file.
  72. */
  73. public function __construct($filename)
  74. {
  75. $reader = new Zend_Io_FileReader($filename);
  76. $this->_magic = $reader->read($reader->getSize());
  77. }
  78. /**
  79. * Returns the recognized MIME type/description of the given file. The type
  80. * is determined by the content using magic bytes characteristic for the
  81. * particular file type.
  82. *
  83. * If the type could not be found, the function returns the default value,
  84. * or <var>null</var>.
  85. *
  86. * @param string $filename The file path whose type to determine.
  87. * @param string $default The default value.
  88. * @return string|false
  89. */
  90. public function getMimeType($filename, $default = null)
  91. {
  92. $reader = new Zend_Io_FileReader($filename);
  93. $parentOffset = 0;
  94. foreach (preg_split('/^/m', $this->_magic) as $line) {
  95. $chunks = array();
  96. if (!preg_match("/^(?P<Dependant>>?)(?P<Byte>\d+)\s+(?P<MatchType" .
  97. ">\S+)\s+(?P<MatchData>\S+)(?:\s+(?P<MIMEType>[a-" .
  98. "z]+\/[a-z-0-9]+)?(?:\s+(?P<Description>.?+))?)?$/",
  99. $line, $chunks)) {
  100. continue;
  101. }
  102. if ($chunks['Dependant']) {
  103. $reader->setOffset($parentOffset);
  104. $reader->skip($chunks['Byte']);
  105. } else {
  106. $reader->setOffset($parentOffset = $chunks['Byte']);
  107. }
  108. $matchType = strtolower($chunks['MatchType']);
  109. $matchData = preg_replace
  110. (array("/\\\\ /", "/\\\\\\\\/", "/\\\\([0-7]{1,3})/e",
  111. "/\\\\x([0-9A-Fa-f]{1,2})/e", "/0x([0-9A-Fa-f]+)/e"),
  112. array(" ", "\\\\",
  113. "pack(\"H*\", base_convert(\"$1\", 8, 16));",
  114. "pack(\"H*\", \"$1\");", "hexdec(\"$1\");"),
  115. $chunks["MatchData"]);
  116. switch ($matchType) {
  117. case 'byte': // single character
  118. $data = $reader->readInt8();
  119. break;
  120. case 'short': // machine-order 16-bit integer
  121. $data = $reader->readInt16();
  122. break;
  123. case 'long': // machine-order 32-bit integer
  124. $data = $reader->readInt32();
  125. break;
  126. case 'string': // arbitrary-length string
  127. $data = $reader->readString8(strlen($matchData));
  128. break;
  129. case 'date': // long integer date (seconds since Unix epoch)
  130. $data = $reader->readInt64BE();
  131. break;
  132. case 'beshort': // big-endian 16-bit integer
  133. $data = $reader->readUInt16BE();
  134. break;
  135. case 'belong': // big-endian 32-bit integer
  136. // break intentionally omitted
  137. case 'bedate': // big-endian 32-bit integer date
  138. $data = $reader->readUInt32BE();
  139. break;
  140. case 'leshort': // little-endian 16-bit integer
  141. $data = $reader->readUInt16LE();
  142. break;
  143. case 'lelong': // little-endian 32-bit integer
  144. // break intentionally omitted
  145. case 'ledate': // little-endian 32-bit integer date
  146. $data = $reader->readUInt32LE();
  147. break;
  148. default:
  149. $data = null;
  150. break;
  151. }
  152. if (strcmp($data, $matchData) == 0) {
  153. if (!empty($chunks['MIMEType'])) {
  154. return $chunks['MIMEType'];
  155. }
  156. if (!empty($chunks['Description'])) {
  157. return rtrim($chunks['Description'], "\n");
  158. }
  159. }
  160. }
  161. return $default;
  162. }
  163. /**
  164. * Returns the results of the mime type check either as a boolean or an
  165. * array of boolean values.
  166. *
  167. * @param string|Array $filename The file path whose type to test.
  168. * @param string|Array $mimeType The mime type to test against.
  169. * @return boolean|Array
  170. */
  171. public function isMimeType($filename, $mimeType)
  172. {
  173. if (is_array($filename)) {
  174. $result = array();
  175. foreach ($filename as $key => $value) {
  176. $result[] =
  177. ($this->getMimeType($value) ==
  178. (is_array($mimeType) ? $mimeType[$key] : $mimeType)) ?
  179. true : false;
  180. }
  181. return $result;
  182. } else {
  183. return $this->getMimeType($filename) == $mimeType ? true : false;
  184. }
  185. }
  186. }