Equation.php 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. <?php
  2. /**
  3. * Equation driver for Text_CAPTCHA.
  4. * Returns simple equations as string, e.g. "9 - 2"
  5. *
  6. * PHP version 5
  7. *
  8. * @category Text
  9. * @package Text_CAPTCHA
  10. * @author Christian Weiske <cweiske@php.net>
  11. * @author Christian Wenz <wenz@php.net>
  12. * @author Michael Cramer <michael@bigmichi1.de>
  13. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  14. * @link http://pear.php.net/package/Text_CAPTCHA
  15. */
  16. require_once 'Text/CAPTCHA/Driver/Base.php';
  17. /**
  18. * Equation driver for Text_CAPTCHA.
  19. * Returns simple equations as string, e.g. "9 - 2"
  20. *
  21. * @category Text
  22. * @package Text_CAPTCHA
  23. * @author Christian Weiske <cweiske@php.net>
  24. * @author Christian Wenz <wenz@php.net>
  25. * @author Michael Cramer <michael@bigmichi1.de>
  26. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  27. * @link http://pear.php.net/package/Text_CAPTCHA
  28. */
  29. class Text_CAPTCHA_Driver_Equation extends Text_CAPTCHA_Driver_Base
  30. {
  31. /**
  32. * Operators that may be used in the equation. Two numbers have to be filled in,
  33. * and %s is needed since number2text conversion may be applied and strings
  34. * filled in.
  35. *
  36. * @var array
  37. */
  38. private $_operators = array(
  39. '%s * %s',
  40. '%s + %s',
  41. '%s - %s',
  42. 'min(%s, %s)',
  43. 'max(%s, %s)'
  44. );
  45. /**
  46. * Minimal number to use in an equation.
  47. *
  48. * @var int
  49. */
  50. private $_min = 1;
  51. /**
  52. * Maximum number to use in an equation.
  53. *
  54. * @var int
  55. */
  56. private $_max = 10;
  57. /**
  58. * Whether numbers shall be converted to text.
  59. *
  60. * @var bool
  61. */
  62. private $_numbersToText = false;
  63. /**
  64. * This variable holds the locale for Numbers_Words.
  65. *
  66. * @var string
  67. */
  68. private $_locale = '';
  69. /**
  70. * Complexity of the generated equations.<br>
  71. * 1 - simple ones such as "1 + 10"<br>
  72. * 2 - harder ones such as "(3-2)*(min(5,6))"
  73. *
  74. * @var int
  75. */
  76. private $_severity = 1;
  77. /**
  78. * Initialize the driver.
  79. *
  80. * @param array $options CAPTCHA options with these keys:<br>
  81. * min minimum numeric value
  82. * max maximum numeric value
  83. * numbersToText boolean for number to text conversion
  84. * locale locale for number to text conversion
  85. * severity number for complexity
  86. *
  87. * @return void
  88. * @throws Text_CAPTCHA_Exception when numbersToText is true, but Number_Words
  89. * package is not available
  90. */
  91. public function initDriver($options = array())
  92. {
  93. if (isset($options['min'])) {
  94. $this->_min = (int)$options['min'];
  95. } else {
  96. $this->_min = 1;
  97. }
  98. if (isset($options['max'])) {
  99. $this->_max = (int)$options['max'];
  100. } else {
  101. $this->_max = 10;
  102. }
  103. if (isset($options['numbersToText'])) {
  104. $this->_numbersToText = (bool)$options['numbersToText'];
  105. } else {
  106. $this->_numbersToText = false;
  107. }
  108. if (isset($options['locale'])) {
  109. $this->_locale = (string)$options['locale'];
  110. } else {
  111. $this->_locale = '';
  112. }
  113. if (isset($options['severity'])) {
  114. $this->_severity = (int)$options['severity'];
  115. } else {
  116. $this->_severity = 1;
  117. }
  118. if ($this->_numbersToText) {
  119. include_once 'Numbers/Words.php';
  120. if (!class_exists('Numbers_Words')) {
  121. throw new Text_CAPTCHA_Exception('Number_Words package required');
  122. }
  123. }
  124. }
  125. /**
  126. * Create random CAPTCHA equation.
  127. * This method creates a random equation.
  128. *
  129. * @return void
  130. * @throws Text_CAPTCHA_Exception when invalid severity is specified
  131. */
  132. public function createCAPTCHA()
  133. {
  134. switch ($this->_severity) {
  135. case 1:
  136. list($equation, $phrase) = $this->_createSimpleEquation();
  137. break;
  138. case 2:
  139. list($eq1, $sol1) = $this->_createSimpleEquation();
  140. list($eq2, $sol2) = $this->_createSimpleEquation();
  141. $op3 = $this->_operators[mt_rand(0, count($this->_operators) - 1)];
  142. list(, $phrase) = $this->_solveSimpleEquation($sol1, $sol2, $op3);
  143. $equation = sprintf($op3, '(' . $eq1 . ')', '(' . $eq2 . ')');
  144. break;
  145. default:
  146. throw new Text_CAPTCHA_Exception(
  147. 'Equation complexity of ' . $this->_severity . ' not supported'
  148. );
  149. }
  150. $this->setCaptcha($equation);
  151. $this->setPhrase($phrase);
  152. }
  153. /**
  154. * Creates a simple equation of type (number operator number).
  155. *
  156. * @return array Array with equation and solution
  157. */
  158. private function _createSimpleEquation()
  159. {
  160. $one = mt_rand($this->_min, $this->_max);
  161. $two = mt_rand($this->_min, $this->_max);
  162. $operator = $this->_operators[mt_rand(0, count($this->_operators) - 1)];
  163. return $this->_solveSimpleEquation($one, $two, $operator);
  164. }
  165. /**
  166. * Solves a simple equation with two given numbers and one operator as defined
  167. * in $this->_operators.
  168. * Also converts the numbers to words if required.
  169. *
  170. * @param int $one First number
  171. * @param int $two Second number
  172. * @param string $operator Operator used with those two numbers
  173. *
  174. * @return array Array with equation and solution
  175. */
  176. private function _solveSimpleEquation($one, $two, $operator)
  177. {
  178. $equation = sprintf($operator, $one, $two);
  179. $function = create_function('', 'return ' . $equation . ';');
  180. if ($this->_numbersToText) {
  181. $numberWords = new Numbers_Words();
  182. $equation = sprintf(
  183. $operator,
  184. $numberWords->toWords($one, $this->_locale),
  185. $numberWords->toWords($two, $this->_locale)
  186. );
  187. }
  188. return array($equation, $function());
  189. }
  190. /**
  191. * Creates the captcha. This method is a placeholder, since the equation is
  192. * created in createCAPTCHA()
  193. *
  194. * @return void
  195. * @see createCAPTCHA()
  196. */
  197. public function createPhrase()
  198. {
  199. $this->setPhrase(null);
  200. }
  201. }