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

/Library/Zend/Validate/Hash.php

https://bitbucket.org/mgatto/zend_validate_hash
PHP | 488 lines | 173 code | 55 blank | 260 comment | 17 complexity | 83fabad8bb77f85adf15cbae865c08e2 MD5 | raw file
  1. <?php
  2. /**
  3. * Contains the definition of the class: Zend_Validate_Hash
  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_Validate
  17. * @copyright Lisantra Technologies, LLC 2009
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @author Michael Gatto <mgatto@lisantra.com>
  20. */
  21. /**
  22. * @see Zend_Validate_Abstract
  23. */
  24. require_once 'Zend/Validate/Abstract.php';
  25. /**
  26. * @see Zend_Validate__Exception
  27. */
  28. require_once 'Zend/Validate/Exception.php';
  29. /**
  30. * Checks if a given hash in hex format is indeed a correctly formatted hash.
  31. * The hash may be in either binary or hexadecimal formats. Various hashing
  32. * algorithms are supported.
  33. *
  34. * @category Zend
  35. * @package Zend_Validate
  36. * @license http://framework.zend.com/license/new-bsd New BSD License
  37. * @copyright 2009
  38. * @author Michael Gatto <mgatto@lisantra.com>
  39. *
  40. * Usage:
  41. * <code>
  42. * $options = array(
  43. * 'algorithm' => 'md5',
  44. * 'format' => 'hex',
  45. * );
  46. * $hasher = new Zend_Validate_Hash($options);
  47. * $result = $hasher->isValid('3be6306716ab9c505374d7cd72c63abd');
  48. * </code>
  49. *
  50. * Alternate Usage:
  51. * <code>
  52. * $hasher = new Zend_Validate_Hash('md5', 'binary');
  53. * $result = $hasher->isValid('3be6306716ab9c505374d7cd72c63abd');
  54. * </code>
  55. */
  56. class Zend_Validate_Hash extends Zend_Validate_Abstract
  57. {
  58. //@todo implement more hashes:
  59. /* This will require checking for Php's hash extension (it replaces mhash,
  60. * which is obsoleted). I should also check for mhash as a fallback, however.
  61. * Php best practice is _not_ to use dl() (deprecated), but rather to issue an
  62. * exception and halt processing.
  63. */
  64. //@todo add error codes to exceptions
  65. /**
  66. * Error type for when the value is not a correctly
  67. * formatted hash
  68. */
  69. const ERROR_NOT_HASH = 'notHash';
  70. /**
  71. * Error type for when the constructor options' format is invalid
  72. */
  73. const ERROR_OPTIONS_INVALID_FORMAT = 'invalidOptions';
  74. /**
  75. * Error type for when Zend_Config_Ini's config not properly formatted
  76. */
  77. const ERROR_OPTIONS_INVALID_INI = 'invalidIni';
  78. /**
  79. * Error type for when the options array does not have an algorithm key
  80. */
  81. const ERROR_OPTIONS_ALGORITHM_KEY_UNDEFINED = 'noAlgorithm';
  82. /**
  83. * Error type for when options array does not have a format key
  84. */
  85. const ERROR_OPTIONS_FORMAT_KEY_UNDEFINED = 'noFormat';
  86. /**
  87. * Error type for when the argument is not in the expected format
  88. */
  89. const ERROR_INPUT_INVALID_FORMAT = 'invalidInput';
  90. /**
  91. * Error type for when the hash is not provided
  92. */
  93. const ERROR_HASH_UNDEFINED = 'undefinedHash';
  94. /**
  95. * Error type for when the algorithm is not provided
  96. */
  97. const ERROR_ALGORITHM_UNDEFINED = 'undefinedAlgorithm';
  98. /**
  99. * Error type for when the algorithm is not a supported hash
  100. */
  101. const ERROR_ALGORITHM_UNSUPPORTED = 'unsupportedAlgorithm';
  102. /**
  103. * Error type for when the format is not provided
  104. */
  105. const ERROR_FORMAT_UNDEFINED = 'undefinedFormat';
  106. /**
  107. * Error type for when the algorithm is not a supported hash
  108. */
  109. const ERROR_FORMAT_UNSUPPORTED = 'unsupportedFormat';
  110. /**
  111. * Currently supported hashes that can be validated.
  112. *
  113. * Currently md5 and sha1, because these functions are in the Php core.
  114. *
  115. * @var array
  116. */
  117. protected $_algorithms = array('md5', 'sha1');
  118. /**
  119. * The hashing algorithm by which the value was generated.
  120. *
  121. * @var string
  122. * @see $_algorithms
  123. */
  124. protected $_algorithm;
  125. /**
  126. * Alternative natural language designations for binary formats.
  127. *
  128. * @var array
  129. */
  130. protected $_binary_formats = array('bin', 'binary');
  131. /**
  132. * Alternative natural language designations for hexadecimal formats.
  133. *
  134. * @var array
  135. */
  136. protected $_hex_formats = array('hex', 'hexadecimal');
  137. /**
  138. * The format of the hash, either binary or hexadecimal
  139. *
  140. * @var string
  141. */
  142. protected $_format;
  143. /**
  144. * The value to be validated
  145. *
  146. * Essentially, I am redeclaring it since this variable also appears in
  147. * Zend_Validate_Abstract.
  148. *
  149. * @var string|binary
  150. */
  151. protected $_value;
  152. /**
  153. * The hash to be evaluated as having a valid format.
  154. *
  155. * Format is not specified. We could specify it by making
  156. * an option to specify binary | hex and / or make hex the default.
  157. *
  158. * @var string|binary
  159. */
  160. protected $_hash;
  161. /**
  162. * Not sure what options one would pass at this point...
  163. *
  164. * @var array
  165. */
  166. protected $_options = array();
  167. /**
  168. * Validation failure message template definitions
  169. *
  170. * @var array is originally defined in Zend_Validate_Hash
  171. * @todo add error templates for when hash too short / chars out of range
  172. */
  173. protected $_messageTemplates = array(
  174. self::ERROR_NOT_HASH => "'%value%' is not a correctly formatted hash",
  175. self::ERROR_OPTIONS_INVALID_FORMAT =>
  176. "The options must be an array",
  177. self::ERROR_OPTIONS_INVALID_INI =>
  178. "The ini options must be within a 'hash.' grouping",
  179. self::ERROR_OPTIONS_FORMAT_KEY_UNDEFINED =>
  180. "The key 'format' was not defined in the options array",
  181. self::ERROR_OPTIONS_ALGORITHM_KEY_UNDEFINED =>
  182. "The key 'algorithm' was not defined in the options array",
  183. self::ERROR_HASH_UNDEFINED => "The hash was not defined",
  184. self::ERROR_ALGORITHM_UNDEFINED => "The algorithm was not defined",
  185. self::ERROR_ALGORITHM_UNSUPPORTED => "This algorithm is unsupported",
  186. self::ERROR_FORMAT_UNDEFINED => "The format was not defined",
  187. self::ERROR_FORMAT_UNSUPPORTED => "This format is unsupported",
  188. self::ERROR_INPUT_INVALID_FORMAT =>
  189. "The hash to validate must be a string",
  190. );
  191. /**
  192. * Class constructor
  193. *
  194. *
  195. *
  196. * @param array $options with keys: 'algorithm' and 'format'
  197. * @return void
  198. */
  199. function __construct($options = null)
  200. {
  201. $this->setOptions($options);
  202. }
  203. /**
  204. * Validate the inputs.
  205. *
  206. * @param string $value
  207. * @return boolean
  208. */
  209. public function isValid($value)
  210. {
  211. /* default value initialized reduces code and possible errors */
  212. $validity = (bool) false;
  213. $this->setHash($value);
  214. /* set the function to validate with, to support many algorithms */
  215. $function = "is_" . $this->getAlgorithm();
  216. if ( ! $this->$function($this->getHash()) ) {
  217. //alternative: if ( ! $validity = $this->$function($this->getHash()) )
  218. /* @see Zend_Validate_Abstract */
  219. $this->_error(self::ERROR_NOT_HASH, $this->getHash());
  220. } else {
  221. $validity = (bool) true;
  222. }
  223. return $validity;
  224. }
  225. /**
  226. * Is the passed value a correctly formatted md5 hash?
  227. *
  228. * @param $hash string which we hope is in md5 format (hexadecimal)
  229. * @return boolean
  230. */
  231. protected function is_md5($hash)
  232. {
  233. /* use a variable so function only has one exit point per Dijkstra */
  234. $result = false;
  235. /* @todo make 2 regexs: one for chars and another to check length
  236. * so we can give more precise error messages.
  237. */
  238. if ( preg_match('/^[0-9a-f]{32}$/', $hash) ) {
  239. $result = true;
  240. }
  241. return (bool) $result;
  242. }
  243. /**
  244. * Is the passed value a correctly formatted sha1 hash?
  245. *
  246. * @param $hash string which we hope is in sha1 format (hexadecimal)
  247. * @return boolean
  248. */
  249. protected function is_sha1($hash)
  250. {
  251. /* use a variable so function only has one exit point per Dijkstra */
  252. $result = false;
  253. /* @todo make 2 regexs: one for chars and another to check length
  254. * so we can give more precise error messages.
  255. */
  256. if ( preg_match('/^[0-9a-f]{40}$/', $hash) ) {
  257. $result = true;
  258. }
  259. return (bool) $result;
  260. }
  261. /**
  262. * Mutator for the algorithm
  263. *
  264. * @param $algorithm string
  265. * @return void
  266. * @see $_algorithm
  267. */
  268. public function setAlgorithm($algorithm)
  269. {
  270. if ( empty($algorithm) ) {
  271. throw new Zend_Validate_Exception(
  272. $this->_messageTemplates[self::ERROR_ALGORITHM_UNDEFINED]
  273. );
  274. }
  275. if ( ! in_array($algorithm, $this->_algorithms) ) {
  276. throw new Zend_Validate_Exception(
  277. $this->_messageTemplates[self::ERROR_ALGORITHM_UNSUPPORTED]
  278. );
  279. }
  280. $this->_algorithm = $algorithm;
  281. }
  282. /**
  283. * Accessor for the hashing algorithm
  284. *
  285. * @return string
  286. * @see $_algorithm
  287. */
  288. public function getAlgorithm()
  289. {
  290. return $this->_algorithm;
  291. }
  292. /**
  293. * Mutator for the options
  294. *
  295. * the 'hash' array must be defined in the ini file;
  296. * Usage:
  297. * <code>
  298. * hash.algorithm = 'md5
  299. * hash.format = 'binary'
  300. *</code>
  301. * @param $options array, scalar or object
  302. * @return void
  303. * @see $_options
  304. */
  305. public function setOptions($options)
  306. {
  307. /* this looks cleaner to me than if/elseif/elseif... */
  308. switch ( true ) {
  309. case $options instanceof Zend_Config:
  310. /* We could say, is_a($options, 'Zend_Config') since is_a() is
  311. * no longer deprecated in Php 5.3
  312. */
  313. if ( ! $options->get('hash') ) {
  314. throw new Zend_Validate_Exception(
  315. $this->_messageTemplates[self::ERROR_OPTIONS_INVALID_INI]
  316. );
  317. }
  318. $this->_options = $options->hash->toArray();
  319. break;
  320. case is_array($options):
  321. $this->_options = $options;
  322. break;
  323. default:
  324. throw new Zend_Validate_Exception(
  325. $this->_messageTemplates[self::ERROR_OPTIONS_INVALID_FORMAT]
  326. );
  327. break;
  328. }
  329. /* algorithm is mandatory */
  330. if ( ! array_key_exists('algorithm', $this->_options) ) {
  331. throw new Zend_Validate_Exception(
  332. $this->_messageTemplates[self::ERROR_OPTIONS_ALGORITHM_KEY_UNDEFINED]
  333. );
  334. }
  335. $this->setAlgorithm($this->_options['algorithm']);
  336. /* format key is mandatory */
  337. if ( ! array_key_exists('format', $this->_options) ) {
  338. throw new Zend_Validate_Exception(
  339. $this->_messageTemplates[self::ERROR_OPTIONS_FORMAT_KEY_UNDEFINED]
  340. );
  341. }
  342. $this->setFormat($this->_options['format']);
  343. }
  344. /**
  345. * Accessor for the options
  346. *
  347. * @return array
  348. * @see $_options
  349. */
  350. public function getOptions()
  351. {
  352. return $this->_options;
  353. }
  354. /**
  355. * Mutator for the format of the hash
  356. *
  357. * @param $format string
  358. * @return void
  359. * @see $_format
  360. */
  361. public function setFormat($format)
  362. {
  363. if ( empty($format) ) {
  364. throw new Zend_Validate_Exception(
  365. $this->_messageTemplates[self::ERROR_FORMAT_UNDEFINED]
  366. );
  367. }
  368. /* set the strict option to true to check for types also */
  369. if ( in_array($format, $this->_hex_formats, true) ) {
  370. $this->_format = 'hexadecimal';
  371. } elseif (in_array($format, $this->_binary_formats, true) ) {
  372. $this->_format = 'binary';
  373. } else {
  374. /* assume nothing; throw an exception */
  375. throw new Zend_Validate_Exception(
  376. $this->_messageTemplates[self::ERROR_FORMAT_UNSUPPORTED]
  377. );
  378. }
  379. }
  380. /**
  381. * Accessor for the format of the hash.
  382. *
  383. * @return string
  384. * @see $_format
  385. */
  386. public function getFormat()
  387. {
  388. return $this->_format;
  389. }
  390. /**
  391. * Mutator for the hash itself
  392. *
  393. * Handles binary hashes by converting to hex before setting it
  394. *
  395. * @param $hash string
  396. * @return void
  397. * @see $_algorithm
  398. */
  399. protected function setHash($hash)
  400. {
  401. //@todo refactor to setValue()
  402. if ( empty($hash) ) {
  403. throw new Zend_Validate_Exception(
  404. $this->_messageTemplates[self::ERROR_HASH_UNDEFINED]
  405. );
  406. }
  407. /* validate the value's type */
  408. if ( ! is_string($hash) ) {
  409. throw new Zend_Validate_Exception(
  410. $this->_messageTemplates[self::ERROR_INPUT_INVALID_FORMAT]
  411. );
  412. }
  413. /* @todo
  414. * I would prefer to use a function to discover its format instead of
  415. * forcing developers to specify it. Php 6.0 has is_binary(), but
  416. * I will otherwise write/copy a user-defined function to this class.
  417. */
  418. if ( $this->getFormat() === 'binary' ) {
  419. $this->_hash = bin2hex($hash);
  420. } else {
  421. $this->_hash = $hash;
  422. }
  423. }
  424. /**
  425. * Returns the hash calculated in the class
  426. *
  427. * @return string|null
  428. */
  429. public function getHash()
  430. {
  431. return $this->_hash;
  432. }
  433. }