2

I am sure that I do not understand something fundamental about Drupal's form API but I hope that someone can help me with that:

I want to create some kind of custom CAPTCHA as the CAPTCHA module states that with caching enabled on your project problems could appear and my project really needs caching via the Boost module. The idea was the following in my forms building array:

  $number1 = rand(0, 10);
  $number2 = rand(0, 10);
  $form['numbercheck'] = array(
    '#type' => 'numberfield',
    '#title' => 'Please fill in the sum of '. $number1 .' and '. $number2,
    '#required' => TRUE,        
  );
  $form_state['storage']['checksum'] = $number1 + $number2;

Then in validation I wanted to do the following check:

 if($form_state['storage']['checksum'] != $form_state['values']['numbercheck']) {
  form_set_error('numbercheck', 'The sum was not correct');  
}

But every time I fill in the form the following happens: say the first random number is 1 and the second number is 2 - I fill in the correct value "3" but get an error "The sum was not correct". Using Devel module and dpm()-function to get the values of $form_state['storage']['checksum'] and the single random numbers I realize that this value changed after submitting the form - while viewing the form the value is correctly "3" but when validation starts the value change to let's say 4 because the then newly created numbers in the then newly created form are maybe 1 and 3.

I tried

$form_state['rebuild'] = FALSE;

in form building function as I thought that the form may be rebuild before validation starts and therefore the random numbers were newly created as well as the $form_state array may be changed but this did not solve my problem.

I hope that somebody can help me out with that - thank you very much in advance!

Best, Tobias

2 Answers 2

1

I don't like the cache method as it is quite complicated. If cache method is the only way to do that. I would recommend using Session Cache API.

In this case, exposing the answer to the form and collecting it from $form_state['input'] would be my choice.

function mymodule_custom_form($form, &$form_state) {
  $input = &$form_state['input'];

  $number1 = rand(0, 10);
  $number2 = rand(0, 10);
  $form['numbercheck'] = array(
    '#type' => 'textfield',
    '#title' => 'Please fill in the sum of '. $number1 .' and '. $number2,
    '#required' => TRUE,
  );

  // hidden field is only hidden to user but you still can inspect it
  // from source. So hash function is used to encrypt here.
  $form['checksum'] = array(
    '#type' => 'hidden',
    '#value' => isset($input['checksum']) ? $input['checksum'] : hash('sha256', $number1 + $number2),
  );

  $form['submit'] = array(
    '#type' => 'submit',
    '#value' => 'Submit',
  );

  return $form;
}

function mymodule_custom_form_validate(&$form, &$form_state) {
  $hash = hash('sha256', $form_state['values']['numbercheck']);

  // compare the hash result.
  if ($form_state['values']['checksum'] != $hash) {
    form_set_error('numbercheck', 'The sum was not correct');  
  }
}
0

What about using cache? That way the sum won't change until the user gets it right:

function test_form($form, &$form_state) {

    if ($cache = cache_get('num1'.$_SERVER['REMOTE_ADDR'])) { // user remote IP so each IP will have a different sum
        $num1 = $cache->data;
    } else {
        $num1 = rand(0, 10);
        cache_set('num1'.$_SERVER['REMOTE_ADDR'], $num1, 'cache');
    }

    if ($cache = cache_get('num2'.$_SERVER['REMOTE_ADDR'])) {
        $num2 = $cache->data;
    } else {
        $num2 = rand(0, 10);
        cache_set('num2'.$_SERVER['REMOTE_ADDR'], $num2, 'cache');
    }

    $form['numbercheck'] = array(
        '#type' => 'textfield',
        '#title' => 'Please fill in the sum of '. $num1 .' and '. $num2,
        '#required' => TRUE,
    );

    $form['submit'] = array(
        '#type' => 'submit',
        '#value' => 'Submit'
    );

    return $form;

}

function test_form_validate($form, &$form_state) {

    $num1 = cache_get('num1'.$_SERVER['REMOTE_ADDR'])->data;
    $num2 = cache_get('num2'.$_SERVER['REMOTE_ADDR'])->data;

    if(($num1 + $num2) != $form_state['values']['numbercheck']) {
        form_set_error('numbercheck', 'The sum was not correct');
    }
}

function test_form_submit($form, &$form_state) {

    // Once they get it right clear the cache so next time the sum will be different
    cache_clear_all('num1'.$_SERVER['REMOTE_ADDR'], 'cache');
    cache_clear_all('num2'.$_SERVER['REMOTE_ADDR'], 'cache');

    die('all done');

}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.