Sanitizing Currency

Hello,

I am currently using an application that stores and retrieves currency from the database. I use the CNumberFormatter class to properly format a number when retrieved and presented in a view, but I noticed there is no method to “un-format” the number when storing it in the database. I searched the forum and found an extension, but I’m not too fond of using extensions for this project.

Since Yii rules validate both client and server side, wouldn’t it just be acceptable to define a rule that only accepts floating point numbers and define something for the beforeValidate() method in the model to sanitize.

I came up with this:




#Validation rules


array('balance', 'required'),

array('balance', 'length', 'max'=>19),

array('balance', 'type', 'type'=>'float', 'message'=>'Should be in the following format: 00.00 (Ex. 12.25)'),	






#Money Model


public function beforeValidate()

{

  if(!empty($this->balance)) {

    $search = array("$",",");

    foreach($search as $nonInteger) {

      $this->balance=str_replace($nonInteger,'',$this->balance);

    }

    return $this->balance;

  }

 return  parent::beforeValidate();											

}



This code in particular would accept currency formatted in U.S. dollars, as well as commas. However, those values would be stripped. Everything else would fail validation. So "$123,456.78", "$123", "123,456.78" would all be valid. Is there a better method to doing this, or would this be considered an acceptable way of implementing what I am looking for?

Thank you in advance!

Cody

If you want to parse the number based on the current locale:




$decimalSeparator = Yii::app()->locale->getNumberSymbol('decimal');

$thousandSeparator = Yii::app()->locale->getNumberSymbol('group');


$this->balance = (float)str_replace(array($thousandSeparator, $decimalSeparator), array('', '.'), $this->balance);



If you want to strip everything but numbers and periods:


$this->balance = (float)preg_replace('/[^\d\.]/', '', $this->balance);

Thank you! This looks like a better way of doing this. The only issue is that if I enter a letter, it will return 0 and pass validation. So if I put "abc" in the text field, it will return with 0 and be inserted into the database with the value of 0. Is there a way to cause it to trigger a failed validation? Thank you for your help!

This is the same approach I would have taken, then I decided to see what formats are out there. If you do a Google search for “currency formats”, you will see that certain formats swap decimal points and commas. Therefore, that approach won’t work for all types, but it will fix 90%.

On an unrelated note, I would have preferred the topic title to be “Laundering Currency” rather than Sanitizing. It’s just more fun, and you’ll get more people looking at your post!

You’re absolutely right, (float) casts any letter to the number 0, and it’s not necessary for the validator or the database query. So this should do just fine:




public function beforeValidate()

{

  $this->balance = preg_replace('/[^\d.]/', '', $this->balance);

  

  return parent::beforeValidate();                                                                                      

}



I know, I’m Dutch and our separators are swapped :) But the first piece of code should fix that, Yii::app()->locale->getNumberSymbol() retrieves the separators based on Yii::app()->language, so if you have a multi-language website, it should accept any format.

Yes, the second piece of code by itself only works when the decimal separator is a period, but Cody only needs to accept US dollars, so then it’s fine.