Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
75.00% |
51 / 68 |
|
57.14% |
4 / 7 |
CRAP | |
0.00% |
0 / 1 |
Captcha | |
75.00% |
51 / 68 |
|
57.14% |
4 / 7 |
54.14 | |
0.00% |
0 / 1 |
__construct | |
63.64% |
7 / 11 |
|
0.00% |
0 / 1 |
9.36 | |||
createNewToken | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
3 | |||
getToken | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
setLabel | |
90.91% |
10 / 11 |
|
0.00% |
0 / 1 |
7.04 | |||
setValidator | |
47.83% |
11 / 23 |
|
0.00% |
0 / 1 |
24.20 | |||
generateEquation | |
100.00% |
13 / 13 |
|
100.00% |
1 / 1 |
6 | |||
evaluateEquation | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | /** |
3 | * Pop PHP Framework (http://www.popphp.org/) |
4 | * |
5 | * @link https://github.com/popphp/popphp-framework |
6 | * @author Nick Sagona, III <dev@nolainteractive.com> |
7 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
8 | * @license http://www.popphp.org/license New BSD License |
9 | */ |
10 | |
11 | /** |
12 | * @namespace |
13 | */ |
14 | namespace Pop\Form\Element\Input; |
15 | |
16 | /** |
17 | * Form CAPTCHA element class |
18 | * |
19 | * @category Pop |
20 | * @package Pop\Form |
21 | * @author Nick Sagona, III <dev@nolainteractive.com> |
22 | * @copyright Copyright (c) 2009-2023 NOLA Interactive, LLC. (http://www.nolainteractive.com) |
23 | * @license http://www.popphp.org/license New BSD License |
24 | * @version 3.6.0 |
25 | */ |
26 | |
27 | class Captcha extends Text |
28 | { |
29 | |
30 | /** |
31 | * Current token data |
32 | * @var array |
33 | */ |
34 | protected $token = []; |
35 | |
36 | /** |
37 | * Constructor |
38 | * |
39 | * Instantiate the captcha input form element |
40 | * |
41 | * @param string $name |
42 | * @param string $value |
43 | * @param string $captcha |
44 | * @param string $answer |
45 | * @param int $expire |
46 | * @param string $indent |
47 | */ |
48 | public function __construct($name, $value = null, $captcha = null, $answer = null, $expire = 300, $indent = null) |
49 | { |
50 | // Start a session. |
51 | if (session_id() == '') { |
52 | session_start(); |
53 | } |
54 | |
55 | // If token does not exist, create one |
56 | if (!isset($_SESSION['pop_captcha']) || (isset($_GET['captcha']) && ((int)$_GET['captcha'] == 1))) { |
57 | $this->createNewToken($captcha, $answer, $expire); |
58 | // Else, retrieve existing token |
59 | } else { |
60 | $this->token = unserialize($_SESSION['pop_captcha']); |
61 | |
62 | // Check to see if the token has expired |
63 | if ($this->token['expire'] > 0) { |
64 | if (($this->token['expire'] + $this->token['start']) < time()) { |
65 | $this->createNewToken($captcha, $value, $expire); |
66 | } |
67 | } |
68 | } |
69 | |
70 | parent::__construct($name, strtoupper((string)$value), $indent); |
71 | $this->setRequired(true); |
72 | $this->setValidator(); |
73 | } |
74 | |
75 | /** |
76 | * Set the token of the CAPTCHA form element |
77 | * |
78 | * @param string $captcha |
79 | * @param string $answer |
80 | * @param int $expire |
81 | * @return Captcha |
82 | */ |
83 | public function createNewToken($captcha = null, $answer = null, $expire = 300) |
84 | { |
85 | if ((null === $captcha) || (null === $answer)) { |
86 | $captcha = $this->generateEquation(); |
87 | $answer = $this->evaluateEquation($captcha); |
88 | } |
89 | |
90 | $this->token = [ |
91 | 'captcha' => $captcha, |
92 | 'answer' => $answer, |
93 | 'expire' => (int)$expire, |
94 | 'start' => time() |
95 | ]; |
96 | $_SESSION['pop_captcha'] = serialize($this->token); |
97 | return $this; |
98 | } |
99 | |
100 | /** |
101 | * Get token |
102 | * |
103 | * @return array |
104 | */ |
105 | public function getToken() |
106 | { |
107 | return $this->token; |
108 | } |
109 | |
110 | /** |
111 | * Set the label of the captcha form element |
112 | * |
113 | * @param string $label |
114 | * @return Captcha |
115 | */ |
116 | public function setLabel($label) |
117 | { |
118 | parent::setLabel($label); |
119 | |
120 | if (isset($this->token['captcha'])) { |
121 | if ((strpos($this->token['captcha'], '<img') === false) && |
122 | ((strpos($this->token['captcha'], ' + ') !== false) || |
123 | (strpos($this->token['captcha'], ' - ') !== false) || |
124 | (strpos($this->token['captcha'], ' * ') !== false) || |
125 | (strpos($this->token['captcha'], ' / ') !== false))) { |
126 | $this->label = $this->label . '(' . |
127 | str_replace([' * ', ' / '], [' × ', ' ÷ '], $this->token['captcha'] .')'); |
128 | } else { |
129 | $this->label = $this->label . $this->token['captcha']; |
130 | } |
131 | } |
132 | |
133 | return $this; |
134 | } |
135 | |
136 | /** |
137 | * Set the validator |
138 | * |
139 | * @throws Exception |
140 | * @return void |
141 | */ |
142 | protected function setValidator() |
143 | { |
144 | $this->validators = []; |
145 | |
146 | // Get query data |
147 | if (!isset($_SERVER['REQUEST_METHOD'])) { |
148 | throw new Exception('Error: The server request method is not set.'); |
149 | } |
150 | |
151 | $queryData = []; |
152 | switch ($_SERVER['REQUEST_METHOD']) { |
153 | case 'GET': |
154 | $queryData = $_GET; |
155 | break; |
156 | |
157 | case 'POST': |
158 | $queryData = $_POST; |
159 | break; |
160 | |
161 | default: |
162 | $input = fopen('php://input', 'r'); |
163 | $qData = null; |
164 | while ($data = fread($input, 1024)) { |
165 | $qData .= $data; |
166 | } |
167 | |
168 | parse_str($qData, $queryData); |
169 | } |
170 | |
171 | // If there is query data, set validator to check against the token value |
172 | if (count($queryData) > 0) { |
173 | if (isset($queryData[$this->name])) { |
174 | $this->addValidator(function($value){ |
175 | $token = $this->getToken(); |
176 | if (isset($token['answer']) && (strtoupper($token['answer']) == strtoupper($value))) { |
177 | return null; |
178 | } else { |
179 | return 'The answer is incorrect.'; |
180 | } |
181 | }); |
182 | } |
183 | } |
184 | } |
185 | |
186 | /** |
187 | * Randomly generate a simple, basic equation |
188 | * |
189 | * @return string |
190 | */ |
191 | protected function generateEquation() |
192 | { |
193 | $ops = [' + ', ' - ', ' * ', ' / ']; |
194 | $equation = null; |
195 | |
196 | $rand1 = rand(1, 10); |
197 | $rand2 = rand(1, 10); |
198 | $op = $ops[rand(0, 3)]; |
199 | |
200 | // If the operator is division, keep the equation very simple, with no remainder |
201 | if ($op == ' / ') { |
202 | $mod = ($rand2 > $rand1) ? $rand2 % $rand1 : $rand1 % $rand2; |
203 | while ($mod != 0) { |
204 | $rand1 = rand(1, 10); |
205 | $rand2 = rand(1, 10); |
206 | $mod = ($rand2 > $rand1) ? $rand2 % $rand1 : $rand1 % $rand2; |
207 | } |
208 | } |
209 | |
210 | $equation = ($rand2 > $rand1) ? $rand2 . $op . $rand1 : $rand1 . $op . $rand2; |
211 | |
212 | return $equation; |
213 | } |
214 | |
215 | /** |
216 | * Evaluate equation |
217 | * |
218 | * @param $equation |
219 | * @return int |
220 | */ |
221 | protected function evaluateEquation($equation) |
222 | { |
223 | return eval("return ($equation);"); |
224 | } |
225 | |
226 | } |