Number groupings

design4effect - September 15, 2009 - 14:12
Project:Social Insurance Number (CCK)
Version:6.x-2.6
Component:Code
Category:task
Priority:normal
Assigned:Unassigned
Status:needs review
Description

Canadian SIN numbers can be expressed as:

  • 123 456 789 (*most common)
  • 123-456-789
  • 123456789

US SIN numbers can be written as separate digit groups spaced with '-' also but rarely are either ever written as a straight string of numbers (other that to accommodate data entry).

I would like users to be able to enter any format they like (as long as it's valid) and the numbers to be expressed in standard format no matter how they were entered.

So I had a look at the sin.ca.inc file.

  function valid_ca_sin($sinstring)

  1. The comments above this function are incorrect... it doesn't return a formatted string it returns a Boolean value.
  2. Using str_split() is expensive, and it's compounded by using a foreach() which makes a copy of the array str_split generated
  3. In the case of the odd digits (doubled) being >=5 str_split is again called to get another array of which [0] could only be 1

The format function didn't do a thing.

I thought I could allow free form input, speed it up, and beautify the output.
Here is a replacement:


/**
* Verification for Canadian Social Insurance Numbers
*
*   accepts '123456789', '123 456 789', or '123-456-789'
*
* @param string $sin
* @return boolean Returns TRUE if valid SIN, else FALSE
*
* Ref: http://research.cs.queensu.ca/~bradbury/checkdigit/sincheck.htm
*/
function valid_ca_sin($sin) {
  $sin = normalize_ca_sin($sin);
  if ($sin) {
    $g0 = $g1 = 0;
    for( $i=0; $i<8; $i++ ) {
      $gn = $sin{$i};
      if ($i & 1)
        $g1 += ($gn<5)? $gn*2: 1 + ($gn-5)*2;
      else
        $g0 += $gn;
    }
    $gn = ($g0 + $g1) % 10;
    if ($sin(8) == 10-$gn) return TRUE;
  }
  return FALSE;
}

/**
* Formatting for Social Insurance Numbers. 
*
* @param string $sin
* @return string Returns a string containting the Social Insurance Number with some formatting.
*/
function format_ca_sin($sin, $field) {
  $s = normalize_ca_sin($sin);
  return $s? substr($s,0,3).' '.substr($s,3,3).' '.substr($s,6,3): "Syntax error: $sin'';
}

/**
* Normalize Canadian SIN to string of 9 digits
*
* @param string $sin
* @return string Returns 9 digit string or FALSE if syntax error
*/
function normalize_ca_sin($sin) {
  $g = strspn($sin,"0123456789");
  if ($g==3) {
    if (($sin{3}===' ' && $sin{7}===' ') || ($sin{3}==='-' && $sin{7}==='-')) {
      $sin = substr($sin,0,3) . substr($sin,4,3) . substr($sin,8,3);
      $g = strspn($sin,"0123456789");
    }
  }
  return ($g==9 && strlen($sin)==9)? $sin: FALSE;
}

The normalize_ca_sin($sin) converts it to a 9 digit string of numbers if it's valid input, else returns FALSE if it's something unexpected.

I used an X & 1 instead of X % 2 and minimized the function calls.
I also used $sin{index} to index characters in the string rather than converting it to an array and using [].

If you would like to allow free form SINs, just replace your sin.ca.inc file with the above listing.

I never changed the US one but the change will be very similar.

- Kent

#1

design4effect - September 15, 2009 - 14:18

Woops... the string "Syntax error:..." should be closed with a double quote, not 2 singles.

Here's the updated listing:

/**
* Verification for Canadian Social Insurance Numbers
*
*   accepts '123456789', '123 456 789', or '123-456-789'
*
* @param string $sin
* @return boolean Returns TRUE if valid SIN, else FALSE
*
* Ref: http://research.cs.queensu.ca/~bradbury/checkdigit/sincheck.htm
*/
function
valid_ca_sin($sin) {
  $sin = normalize_ca_sin($sin);
  if ($sin) {
    $g0 = $g1 = 0;
    for( $i=0; $i<8; $i++ ) {
      $gn = $sin{$i};
      if ($i & 1)
        $g1 += ($gn<5)? $gn*2: 1 + ($gn-5)*2;
      else
        $g0 += $gn;
    }
    $gn = ($g0 + $g1) % 10;
    if ($sin(8) == 10-$gn) return TRUE;
  }
  return FALSE;
}

/**
* Formatting for Social Insurance Numbers.
*
* @param string $sin
* @return string Returns a string containting the Social Insurance Number with some formatting.
*/
function format_ca_sin($sin, $field) {
  $s = normalize_ca_sin($sin);
  return $s? substr($s,0,3).' '.substr($s,3,3).' '.substr($s,6,3): "Syntax error: $sin";
}

/**
* Normalize Canadian SIN to string of 9 digits
*
* @param string $sin
* @return string Returns 9 digit string or FALSE if syntax error
*/
function normalize_ca_sin($sin) {
  $g = strspn($sin,"0123456789");
  if ($g==3) {
    if (($sin{3}===' ' && $sin{7}===' ') || ($sin{3}==='-' && $sin{7}==='-')) {
      $sin = substr($sin,0,3) . substr($sin,4,3) . substr($sin,8,3);
      $g = strspn($sin,"0123456789");
    }
  }
  return ($g==9 && strlen($sin)==9)? $sin: FALSE;
}

- Kent

 
 

Drupal is a registered trademark of Dries Buytaert.