You are here

function webform_validation_webform_validation_validate in Webform Validation 7

Same name and namespace in other branches
  1. 6 webform_validation.validators.inc \webform_validation_webform_validation_validate()

Implements hook_webform_validation_validate().

1 call to webform_validation_webform_validation_validate()
WebformValidationUnitTestCase::test in tests/WebformValidationUnitTestCase.test
The tests.

File

./webform_validation.validators.inc, line 471
Provides validation functionality and hooks.

Code

function webform_validation_webform_validation_validate($validator_name, $items, $components, $rule) {
  if (!$items) {
    return NULL;
  }

  // For certain validators, if all components to be compared are email
  // components, make the values lower-case to avoid case-sensitive comparison.
  if (in_array($validator_name, array(
    'equal',
    'comparison',
    'unique',
  ))) {
    $all_email = TRUE;
    foreach ($rule['components'] as $component) {
      if ($component['type'] !== 'email') {
        $all_email = FALSE;
        break;
      }
    }
    if ($all_email) {
      $items = array_map('strtolower', $items);
    }
  }

  // Some components, such as multiple select, send their values as arrays,
  // others don't. Convert all to arrays and write the rules to handle them that
  // way. Remove empty values.
  foreach ($items as $key => $val) {
    $new_values = array();
    foreach ((array) $val as $val_key => $val_val) {
      if ((string) $val_val !== '') {
        $new_values[$val_key] = $val_val;
      }
    }
    $items[$key] = $new_values;
  }

  // Ensure later calls to key() get the first item.
  reset($items);
  $errors = array();
  switch ($validator_name) {
    case 'numeric':
      $num_range = _webform_numeric_check_data($rule['data']);
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {

          // First check if the value is numeric.
          if (is_numeric($subval)) {
            $subval = (double) $subval;
          }
          else {
            $errors[$key] = t('%item is not numeric.', array(
              '%item' => $components[$key]['name'],
            ));
            continue;
          }

          // Now validate the entered numeric value against the validator range
          // settings, if appropriate.
          // a. validate min & max.
          if (isset($num_range['min']) && isset($num_range['max'])) {

            // Validate the min - max range.
            if ($subval < $num_range['min'] || $subval > $num_range['max']) {
              $options = array(
                '%item' => $components[$key]['name'],
                '%range' => str_replace('|', ' - ', $rule['data']),
              );
              $errors[$key] = t('%item is not within the allowed range %range.', $options);
            }
          }
          else {

            // b. validate min.
            if (isset($num_range['min'])) {
              if ($subval < $num_range['min']) {
                $errors[$key] = t('%item should be greater than or equal to %val.', array(
                  '%item' => $components[$key]['name'],
                  '%val' => $num_range['min'],
                ));
              }
            }

            // c. validate max.
            if (isset($num_range['max'])) {
              if ($subval > $num_range['max']) {
                $errors[$key] = t('%item should be less than or equal to %val.', array(
                  '%item' => $components[$key]['name'],
                  '%val' => $num_range['max'],
                ));
              }
            }
          }
        }
      }
      return $errors;
    case 'min_length':
      $min_length = $rule['data'];
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {
          if (drupal_strlen($subval) < $min_length) {
            $errors[$key] = t('%item should be at least %num characters long.', array(
              '%item' => $components[$key]['name'],
              '%num' => $min_length,
            ));
          }
        }
      }
      return $errors;
    case 'max_length':
      $max_length = $rule['data'];
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {
          if (drupal_strlen($subval) > $max_length) {
            $errors[$key] = t('%item should be at most %num characters long.', array(
              '%item' => $components[$key]['name'],
              '%num' => $max_length,
            ));
          }
        }
      }
      return $errors;
    case 'min_words':
      $min_words = $rule['data'];
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {
          if (_webform_validation_count_words($subval) < $min_words) {
            $error = format_plural($min_words, '%item should be at least 1 word long.', '%item should be at least @count words long.', array(
              '%item' => $components[$key]['name'],
            ));
            $errors[$key] = $error;
          }
        }
      }
      return $errors;
    case 'max_words':
      $max_words = $rule['data'];
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {
          if (_webform_validation_count_words($subval) > $max_words) {
            $error = format_plural($max_words, '%item should be at most 1 word long.', '%item should be at most @count words long.', array(
              '%item' => $components[$key]['name'],
            ));
            $errors[$key] = $error;
          }
        }
      }
      return $errors;
    case 'sum':

      // Get the numbers to sum.
      $sum = array();
      foreach ($items as $item) {
        if (isset($item[0])) {
          $sum[] = $item[0];
        }
      }

      // If none of the components are completed, don't run this validator.
      if (!count($sum)) {
        return array();
      }
      $sum = array_sum($sum);

      // Number being compared with.
      $compare_number = (double) preg_replace('/^[^0-9]+/', '', $rule['data']);

      // Parse the comparision operator and do comparison.
      module_load_include('inc', 'webform', 'components/number');
      $error = FALSE;
      if (substr($rule['data'], 0, 2) === '<=') {
        if (!(webform_compare_floats($sum, $compare_number) <= 0)) {
          $error = t('less than or equal to');
        }
      }
      elseif (substr($rule['data'], 0, 1) === '<') {
        if (!(webform_compare_floats($sum, $compare_number) < 0)) {
          $error = t('less than');
        }
      }
      elseif (substr($rule['data'], 0, 2) === '>=') {
        if (!(webform_compare_floats($sum, $compare_number) >= 0)) {
          $error = t('greater than or equal to');
        }
      }
      elseif (substr($rule['data'], 0, 1) === '>') {
        if (!(webform_compare_floats($sum, $compare_number) > 0)) {
          $error = t('greater than');
        }
      }
      else {
        if (!(webform_compare_floats($sum, $compare_number) === 0)) {
          $error = t('exactly');
        }
      }

      // Set error if needed.
      if ($error) {
        $keys = array_keys($items);
        $errors[$keys[0]] = t('These items must add up to %verb %compare_number:', array(
          '%verb' => $error,
          '%compare_number' => $compare_number,
        )) . theme('item_list', array(
          'items' => _webform_validation_get_names_of_rule_components($rule),
        ));
      }
      return $errors;
    case 'equal':
      $first_entry_key = key($items);
      $first_entry = array_shift($items);
      foreach ($items as $key => $val) {
        if ($val !== $first_entry) {
          $errors[$key] = t('%item_checked does not match %item_first.', array(
            '%item_checked' => $components[$key]['name'],
            '%item_first' => $components[$first_entry_key]['name'],
          ));
        }
      }
      return $errors;
    case 'comparison':
      foreach (array(
        1,
        2,
      ) as $count) {
        $entry[$count]['key'] = key($items);
        $value = array_shift($items);
        if ($components[$entry[$count]['key']]['type'] === 'date') {
          if (!empty($value) && checkdate((int) $value['month'], (int) $value['day'], (int) $value['year'])) {
            $entry[$count]['value'] = date('Y-m-d', mktime(0, 0, 0, (int) $value['month'], (int) $value['day'], (int) $value['year']));
          }
          else {
            $entry[$count]['value'] = NULL;
          }
        }
        elseif ($components[$entry[$count]['key']]['type'] === 'time') {
          if (isset($value['hour']) && isset($value['minute'])) {
            $time = $value['hour'] . ':' . str_pad($value['minute'], 2, '0', STR_PAD_LEFT);
            if (!empty($value['ampm'])) {
              $time .= ' ' . $value['ampm'];
            }
            $time = strtotime($time);
          }
          else {
            $time = NULL;
          }
          if ($time) {
            $entry[$count]['value'] = date('H:i', $time);
          }
          else {
            $entry[$count]['value'] = NULL;
          }
        }
        else {
          $entry[$count]['value'] = _webform_validation_flatten_array($value);
        }
      }

      // Don't validate if either are empty.
      if ((string) $entry[1]['value'] === '' || (string) $entry[2]['value'] === '') {
        return array();
      }
      $error = FALSE;
      switch ($rule['data']) {
        case '>':
          if (!($entry[1]['value'] > $entry[2]['value'])) {
            $error = TRUE;
          }
          break;
        case '>=':
          if (!($entry[1]['value'] >= $entry[2]['value'])) {
            $error = TRUE;
          }
          break;
        case '<':
          if (!($entry[1]['value'] < $entry[2]['value'])) {
            $error = TRUE;
          }
          break;
        case '<=':
          if (!($entry[1]['value'] <= $entry[2]['value'])) {
            $error = TRUE;
          }
          break;
      }
      if ($error) {
        $errors[$entry[2]['key']] = _webform_validation_i18n_error_message($rule);
      }
      return $errors;
    case 'unique':
      foreach ($items as $key => $val) {
        $items[$key] = _webform_validation_flatten_array($val);
      }

      // Now we count how many times each value appears, and find out which
      // values appear more than once.
      $items_count = array_count_values(array_map('drupal_strtolower', array_map('trim', $items)));
      $doubles = array_filter($items_count, function ($x) {
        return $x > 1;
      });
      foreach ($items as $key => $val) {
        if (in_array(drupal_strtolower($val), array_keys($doubles))) {
          $errors[$key] = t('The value of %item is not unique.', array(
            '%item' => $components[$key]['name'],
          ));
        }
      }
      return $errors;
    case 'specific_value':
      $specific_values = explode(',', $rule['data']);
      $specific_values = array_map('trim', $specific_values);
      foreach ($items as $key => $val) {

        // Selects: Fail if at least one checked and at least one not in the
        // allowed values.
        $val = array_filter($val);
        $test = count($val) && count(array_diff($val, $specific_values));
        _webform_validation_test($errors, $key, $rule, $test);
      }
      return $errors;
    case 'default_value':
      foreach ($items as $key => $val) {
        $val = _webform_validation_flatten_array($val);
        $test = $val != $components[$key]['value'];
        _webform_validation_test($errors, $key, $rule, $test);
      }
      return $errors;
    case 'someofseveral':

      // Determine the number of components completed.
      foreach ($items as $key => $val) {
        $items[$key] = _webform_validation_flatten_array($val);
        $items[$key] = (bool) strlen($items[$key]);
      }
      $number_completed = count(array_filter($items));

      // Number being compared with.
      $compare_number = (int) preg_replace('/[^0-9]+/', '', $rule['data']);
      if ($compare_number < 1) {
        $compare_number = 1;
      }
      elseif ($compare_number > count($rule['components'])) {
        $compare_number = count($rule['components']);
      }

      // Parse the comparision operator and do comparison.
      $error = FALSE;
      if (substr($rule['data'], 0, 1) === '=') {
        if (!($number_completed === $compare_number)) {
          $error = t('exactly');
        }
      }
      elseif (substr($rule['data'], 0, 2) === '<=') {
        if (!($number_completed <= $compare_number)) {
          $error = t('at most');
        }
      }
      else {
        if (!($number_completed >= $compare_number)) {
          $error = t('at least');
        }
      }

      // Set error if needed.
      if ($error) {
        $keys = array_keys($items);
        $errors[$keys[0]] = t('You must complete %verb %compare_number of these items:', array(
          '%verb' => $error,
          '%compare_number' => $compare_number,
        )) . theme('item_list', array(
          'items' => _webform_validation_get_names_of_rule_components($rule),
        ));
      }
      return $errors;
    case 'select_min':
      $min_selections = $rule['data'];
      foreach ($items as $key => $val) {
        if (count(array_filter($val, '_webform_validation_check_false')) < $min_selections) {
          $errors[$key] = t('Please select at least %num options for %item.', array(
            '%num' => $min_selections,
            '%item' => $components[$key]['name'],
          ));
        }
      }
      return $errors;
    case 'select_max':
      $max_selections = $rule['data'];
      foreach ($items as $key => $val) {
        if (count(array_filter($val, '_webform_validation_check_false')) > $max_selections) {
          $errors[$key] = t('Please select maximum %num options for %item.', array(
            '%num' => $max_selections,
            '%item' => $components[$key]['name'],
          ));
        }
      }
      return $errors;
    case 'select_exact':
      $allowed_selections = $rule['data'];
      foreach ($items as $key => $val) {
        $error_strings = array(
          'regular' => 'Please select %num options for %item.',
          'negated' => 'Please do not select %num options for %item.',
        );
        $error_vars = array(
          '%num' => $allowed_selections,
          '%item' => $components[$key]['name'],
        );
        $test = count(array_filter($val, '_webform_validation_check_false')) != $allowed_selections;
        _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
      }
      return $errors;
    case 'plain_text':
      foreach ($items as $key => $val) {
        $error_strings = array(
          'regular' => '%item only allows the use of plain text.',
          'negated' => '%item must contain HTML tags.',
        );
        $error_vars = array(
          '%item' => $components[$key]['name'],
        );
        foreach ($val as $subval) {
          $test = strcmp($subval, strip_tags($subval));
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
        }
      }
      return $errors;
    case 'starts_with':
    case 'ends_with':
      $pattern = preg_quote($rule['data'], '/');
      if ($validator_name === 'starts_with') {
        $pattern = '^' . $pattern;
        $verb = t('start');
      }
      else {
        $pattern .= '$';
        $verb = t('end');
      }
      $pattern = '/' . $pattern . '/';
      foreach ($items as $key => $val) {
        $error_strings = array(
          'regular' => '%item must %verb with "%string".',
          'negated' => '%item must not %verb with "%string".',
        );
        $error_vars = array(
          '%item' => $components[$key]['name'],
          '%verb' => $verb,
          '%string' => $rule['data'],
        );
        foreach ($val as $subval) {
          $test = !preg_match($pattern, $subval);
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
        }
      }
      return $errors;
    case 'pattern':
      $pattern = preg_quote($rule['data'], '/');
      $pattern = str_replace('@', '[a-zA-Z]', $pattern);

      // Since PHP 7.3, the # character is quoted by preg_quote().
      $quoted_hash = version_compare(PHP_VERSION, '7.3.0', '>=') ? '\\#' : '#';
      $pattern = str_replace($quoted_hash, '[0-9]', $pattern);
      $pattern = '(' . $pattern . ')';

      // Un-escape "|" operator.
      $pattern = preg_replace('/(\\s*)(\\\\)(\\|)(\\s*)/', ')|(', $pattern);
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {
          $test = !preg_match('/^(' . $pattern . ')$/', $subval);
          _webform_validation_test($errors, $key, $rule, $test);
        }
      }
      return $errors;
    case 'regex':
    case 'regexi':
      mb_regex_encoding('UTF-8');
      $regex = $rule['data'];
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {
          if ($validator_name === 'regexi') {
            $match = (bool) mb_eregi($regex, $subval);
          }
          else {
            $match = (bool) mb_ereg($regex, $subval);
          }
          $test = !$match;
          _webform_validation_test($errors, $key, $rule, $test);
        }
      }
      return $errors;
    case 'must_be_empty':
      foreach ($items as $key => $val) {
        if (count($val) !== 0) {
          $errors[$key] = t('%item does not contain the correct data.', array(
            '%item' => $components[$key]['name'],
          ));
        }
      }
      return $errors;
    case 'blacklist':
      $blacklist = $rule['data'];
      $blacklist = token_replace($blacklist);
      $blacklist = preg_quote($blacklist, '/');
      $blacklist = explode(',', $blacklist);
      $blacklist = array_map('trim', $blacklist);
      $blacklist = '/\\b(' . implode('|', $blacklist) . ')\\b/i';
      foreach ($items as $key => $val) {
        foreach ($val as $subval) {
          $test = preg_match($blacklist, $subval);
          _webform_validation_test($errors, $key, $rule, $test);
        }
      }
      return $errors;
    case 'username':
      foreach ($items as $key => $val) {
        $error_strings = array(
          'regular' => 'The %item field does not match an active username.',
          'negated' => 'The %item field matches an active username.',
        );
        $error_vars = array(
          '%item' => $components[$key]['name'],
        );
        foreach ($val as $subval) {
          $test = !db_query_range('SELECT 1 FROM {users} WHERE name = :username AND status = 1', 0, 1, array(
            ':username' => $subval,
          ))
            ->fetchField();
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
        }
      }
      return $errors;
    case 'valid_url':
      foreach ($items as $key => $val) {
        $error_strings = array(
          'regular' => '%item does not appear to be a valid URL.',
          'negated' => '%item must not be a valid URL.',
        );
        $error_vars = array(
          '%item' => $components[$key]['name'],
        );
        foreach ($val as $subval) {
          $test = !valid_url($subval, TRUE);
          _webform_validation_test($errors, $key, $rule, $test, $error_strings, $error_vars);
        }
      }
      return $errors;
    case 'email_verify':
      if (module_exists('email_verify')) {
        foreach ($items as $key => $val) {
          foreach ($val as $subval) {
            $error = email_verify_check($subval);
            if ($error) {
              $errors[$key] = $error;
            }
          }
        }
      }
      return $errors;
  }
}