Validate IBAN PHP - php

As designing a new platform we tried to integrate the IBAN numbers. We have to make sure that the IBAN is validated and the IBAN stored to the database is always correct. So what would be a proper way to validate the number?

As the logic was explained in my other question, I've created a function myself. Based on the logic explained in the Wikipedia article find a proper function below. Country specific validation.
Algorithm and character lengths per country at https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN.
function checkIBAN($iban)
{
if(strlen($iban) < 5) return false;
$iban = strtolower(str_replace(' ','',$iban));
$Countries = array('al'=>28,'ad'=>24,'at'=>20,'az'=>28,'bh'=>22,'be'=>16,'ba'=>20,'br'=>29,'bg'=>22,'cr'=>21,'hr'=>21,'cy'=>28,'cz'=>24,'dk'=>18,'do'=>28,'ee'=>20,'fo'=>18,'fi'=>18,'fr'=>27,'ge'=>22,'de'=>22,'gi'=>23,'gr'=>27,'gl'=>18,'gt'=>28,'hu'=>28,'is'=>26,'ie'=>22,'il'=>23,'it'=>27,'jo'=>30,'kz'=>20,'kw'=>30,'lv'=>21,'lb'=>28,'li'=>21,'lt'=>20,'lu'=>20,'mk'=>19,'mt'=>31,'mr'=>27,'mu'=>30,'mc'=>27,'md'=>24,'me'=>22,'nl'=>18,'no'=>15,'pk'=>24,'ps'=>29,'pl'=>28,'pt'=>25,'qa'=>29,'ro'=>24,'sm'=>27,'sa'=>24,'rs'=>22,'sk'=>24,'si'=>19,'es'=>24,'se'=>24,'ch'=>21,'tn'=>24,'tr'=>26,'ae'=>23,'gb'=>22,'vg'=>24);
$Chars = array('a'=>10,'b'=>11,'c'=>12,'d'=>13,'e'=>14,'f'=>15,'g'=>16,'h'=>17,'i'=>18,'j'=>19,'k'=>20,'l'=>21,'m'=>22,'n'=>23,'o'=>24,'p'=>25,'q'=>26,'r'=>27,'s'=>28,'t'=>29,'u'=>30,'v'=>31,'w'=>32,'x'=>33,'y'=>34,'z'=>35);
if(array_key_exists(substr($iban,0,2), $Countries) && strlen($iban) == $Countries[substr($iban,0,2)]){
$MovedChar = substr($iban, 4).substr($iban,0,4);
$MovedCharArray = str_split($MovedChar);
$NewString = "";
foreach($MovedCharArray AS $key => $value){
if(!is_numeric($MovedCharArray[$key])){
if(!isset($Chars[$MovedCharArray[$key]])) return false;
$MovedCharArray[$key] = $Chars[$MovedCharArray[$key]];
}
$NewString .= $MovedCharArray[$key];
}
if(bcmod($NewString, '97') == 1)
{
return true;
}
}
return false;
}

Slight modification of #PeterFox answer including support for bcmod() when bcmath is not available,
<?php
function isValidIBAN ($iban) {
$iban = strtolower($iban);
$Countries = array(
'al'=>28,'ad'=>24,'at'=>20,'az'=>28,'bh'=>22,'be'=>16,'ba'=>20,'br'=>29,'bg'=>22,'cr'=>21,'hr'=>21,'cy'=>28,'cz'=>24,
'dk'=>18,'do'=>28,'ee'=>20,'fo'=>18,'fi'=>18,'fr'=>27,'ge'=>22,'de'=>22,'gi'=>23,'gr'=>27,'gl'=>18,'gt'=>28,'hu'=>28,
'is'=>26,'ie'=>22,'il'=>23,'it'=>27,'jo'=>30,'kz'=>20,'kw'=>30,'lv'=>21,'lb'=>28,'li'=>21,'lt'=>20,'lu'=>20,'mk'=>19,
'mt'=>31,'mr'=>27,'mu'=>30,'mc'=>27,'md'=>24,'me'=>22,'nl'=>18,'no'=>15,'pk'=>24,'ps'=>29,'pl'=>28,'pt'=>25,'qa'=>29,
'ro'=>24,'sm'=>27,'sa'=>24,'rs'=>22,'sk'=>24,'si'=>19,'es'=>24,'se'=>24,'ch'=>21,'tn'=>24,'tr'=>26,'ae'=>23,'gb'=>22,'vg'=>24
);
$Chars = array(
'a'=>10,'b'=>11,'c'=>12,'d'=>13,'e'=>14,'f'=>15,'g'=>16,'h'=>17,'i'=>18,'j'=>19,'k'=>20,'l'=>21,'m'=>22,
'n'=>23,'o'=>24,'p'=>25,'q'=>26,'r'=>27,'s'=>28,'t'=>29,'u'=>30,'v'=>31,'w'=>32,'x'=>33,'y'=>34,'z'=>35
);
if (strlen($iban) != $Countries[ substr($iban,0,2) ]) { return false; }
$MovedChar = substr($iban, 4) . substr($iban,0,4);
$MovedCharArray = str_split($MovedChar);
$NewString = "";
foreach ($MovedCharArray as $k => $v) {
if ( !is_numeric($MovedCharArray[$k]) ) {
$MovedCharArray[$k] = $Chars[$MovedCharArray[$k]];
}
$NewString .= $MovedCharArray[$k];
}
if (function_exists("bcmod")) { return bcmod($NewString, '97') == 1; }
// http://au2.php.net/manual/en/function.bcmod.php#38474
$x = $NewString; $y = "97";
$take = 5; $mod = "";
do {
$a = (int)$mod . substr($x, 0, $take);
$x = substr($x, $take);
$mod = $a % $y;
}
while (strlen($x));
return (int)$mod == 1;
}

The accepted answer is not the preferred way of validation. The specification dictates the following:
Check that the total IBAN length is correct as per the country. If not, the IBAN is invalid
Replace the two check digits by 00 (e.g. GB00 for the UK)
Move the four initial characters to the end of the string
Replace the letters in the string with digits, expanding the string as necessary, such that A or a = 10, B or b = 11, and Z or z = 35. Each alphabetic character is therefore replaced by 2 digits
Convert the string to an integer (i.e. ignore leading zeroes)
Calculate mod-97 of the new number, which results in the remainder
Subtract the remainder from 98, and use the result for the two check digits. If the result is a single digit number, pad it with a leading 0 to make a two-digit number
I've written a class that validates, formats and parses strings according to the spec. Hope this helps some save the time required to roll their own.
The code can be found on GitHub here.

top rated function does NOT work.
Just try a string with '%' in it...
I'm using this one :
function checkIBAN($iban) {
// Normalize input (remove spaces and make upcase)
$iban = strtoupper(str_replace(' ', '', $iban));
if (preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $iban)) {
$country = substr($iban, 0, 2);
$check = intval(substr($iban, 2, 2));
$account = substr($iban, 4);
// To numeric representation
$search = range('A','Z');
foreach (range(10,35) as $tmp)
$replace[]=strval($tmp);
$numstr=str_replace($search, $replace, $account.$country.'00');
// Calculate checksum
$checksum = intval(substr($numstr, 0, 1));
for ($pos = 1; $pos < strlen($numstr); $pos++) {
$checksum *= 10;
$checksum += intval(substr($numstr, $pos,1));
$checksum %= 97;
}
return ((98-$checksum) == $check);
} else
return false;
}

I found this solution in cakephp 3.7 validation class. Plain beautiful php realization.
/**
* Check that the input value has a valid International Bank Account Number IBAN syntax
* Requirements are uppercase, no whitespaces, max length 34, country code and checksum exist at right spots,
* body matches against checksum via Mod97-10 algorithm
*
* #param string $check The value to check
*
* #return bool Success
*/
public static function iban($check)
{
if (!preg_match('/^[A-Z]{2}[0-9]{2}[A-Z0-9]{1,30}$/', $check)) {
return false;
}
$country = substr($check, 0, 2);
$checkInt = intval(substr($check, 2, 2));
$account = substr($check, 4);
$search = range('A', 'Z');
$replace = [];
foreach (range(10, 35) as $tmp) {
$replace[] = strval($tmp);
}
$numStr = str_replace($search, $replace, $account . $country . '00');
$checksum = intval(substr($numStr, 0, 1));
$numStrLength = strlen($numStr);
for ($pos = 1; $pos < $numStrLength; $pos++) {
$checksum *= 10;
$checksum += intval(substr($numStr, $pos, 1));
$checksum %= 97;
}
return ((98 - $checksum) === $checkInt);
}

This function check the IBAN and need GMP activate http://php.net/manual/en/book.gmp.php.
function checkIban($string){
$to_check = substr($string, 4).substr($string, 0,4);
$converted = '';
for ($i = 0; $i < strlen($to_check); $i++){
$char = strtoupper($to_check[$i]);
if(preg_match('/[0-9A-Z]/',$char)){
if(!preg_match('/\d/',$char)){
$char = ord($char)-55;
}
$converted .= $char;
}
}
// prevent: "gmp_mod() $num1 is not an integer string" error
$converted = ltrim($converted, '0');
return strlen($converted) && gmp_strval(gmp_mod($converted, '97')) == 1;
}
enjoy !

Related

Rewrite a large number of for loops into something shorter

I have the following code:
for($a=1; $a<strlen($string); $a++){
for($b=1; $a+$b<strlen($string); $b++){
for($c=1; $a+$b+$c<strlen($string); $c++){
for($d=1; $a+$b+$c+$d<strlen($string); $d++){
$tempString = substr_replace($string, ".", $a, 0);
$tempString = substr_replace($tempString, ".", $a+$b+1, 0);
$tempString = substr_replace($tempString, ".", $a+$b+$c+2, 0);
$tempString = substr_replace($tempString, ".", $a+$b+$c+$d+3, 0);
echo $tempString."</br>";
}
}
}
}
What it does is to make all possible combinatons of a string with several dots.
Example:
t.est123
te.st123
tes.t123
...
test12.3
Then, I add one more dot:
t.e.st123
t.es.t123
...
test1.2.3
Doing the way I'm doing now, I need to create lots and lots of for loops, each for a determined number of dots. I don't know how I can turn that example into a functon or other easier way of doing this.
Your problem is a combination problem. Note: I'm not a math freak, I only researched this information because of interest.
http://en.wikipedia.org/wiki/Combination#Number_of_k-combinations
Also known as n choose k. The Binomial coefficient is a function which gives you the number of combinations.
A function I found here: Calculate value of n choose k
function choose($n, $k) {
if ($k == 0) {return 1;}
return($n * choose($n - 1, $k - 1)) / $k;
}
// 6 positions between characters (test123), 4 dots
echo choose(6, 4); // 15 combinations
To get all combinations you also have to choose between different algorithms.
Good post: https://stackoverflow.com/a/127856/1948627
UPDATE:
I found a site with an algorithm in different programming languages. (But not PHP)
I've converted it to PHP:
function bitprint($u){
$s= [];
for($n= 0;$u > 0;++$n, $u>>= 1) {
if(($u & 1) > 0) $s[] = $n;
}
return $s;
}
function bitcount($u){
for($n= 0;$u > 0;++$n, $u&= ($u - 1));
return $n;
}
function comb($c, $n){
$s= [];
for($u= 0;$u < 1 << $n;$u++) {
if(bitcount($u) == $c) $s[] = bitprint($u);
}
return $s;
}
echo '<pre>';
print_r(comb(4, 6));
It outputs an array with all combinations (positions between the chars).
The next step is to replace the string with the dots:
$string = 'test123';
$sign = '.';
$combs = comb(4, 6);
// get all combinations (Th3lmuu90)
/*
$combs = [];
for($i=0; $i<strlen($string); $i++){
$combs = array_merge($combs, comb($i, strlen($string)-1));
}
*/
foreach ($combs as $comb) {
$a = $string;
for ($i = count($comb) - 1; $i >= 0; $i--) {
$a = substr_replace($a, $sign, $comb[$i] + 1, 0);
}
echo $a.'<br>';
}
// output:
t.e.s.t.123
t.e.s.t1.23
t.e.st.1.23
t.es.t.1.23
te.s.t.1.23
t.e.s.t12.3
t.e.st.12.3
t.es.t.12.3
te.s.t.12.3
t.e.st1.2.3
t.es.t1.2.3
te.s.t1.2.3
t.est.1.2.3
te.st.1.2.3
tes.t.1.2.3
This is quite an unusual question, but I can't help but try to wrap around what you are tying to do. My guess is that you want to see how many combinations of a string there are with a dot moving between characters, finally coming to rest right before the last character.
My understanding is you want a count and a printout of string similar to what you see here:
t.est
te.st
tes.t
t.es.t
te.s.t
t.e.s.t
count: 6
To facilitate this functionality I came up with a class, this way you could port it to other parts of code and it can handle multiple strings. The caveat here is the strings must be at least two characters and not contain a period. Here is the code for the class:
class DotCombos
{
public $combos;
private function combos($string)
{
$rebuilt = "";
$characters = str_split($string);
foreach($characters as $index => $char) {
if($index == 0 || $index == count($characters)) {
continue;
} else if(isset($characters[$index]) && $characters[$index] == ".") {
break;
} else {
$rebuilt = substr($string, 0, $index) . "." . substr($string, $index);
print("$rebuilt\n");
$this->combos++;
}
}
return $rebuilt;
}
public function allCombos($string)
{
if(strlen($string) < 2) {
return null;
}
$this->combos = 0;
for($i = 0; $i < count(str_split($string)) - 1; $i++) {
$string = $this->combos($string);
}
}
}
To make use of the class you would do this:
$combos = new DotCombos();
$combos->allCombos("test123");
print("Count: $combos->combos");
The output would be:
t.est123
te.st123
tes.t123
test.123
test1.23
test12.3
t.est12.3
te.st12.3
tes.t12.3
test.12.3
test1.2.3
t.est1.2.3
te.st1.2.3
tes.t1.2.3
test.1.2.3
t.est.1.2.3
te.st.1.2.3
tes.t.1.2.3
t.es.t.1.2.3
te.s.t.1.2.3
t.e.s.t.1.2.3
Count: 21
Hope that is what you are looking for (or at least helps)....

getting a substring(charset independent) between given two offsets

I just want to know if there is any built in php function where I can get a substring between given two keywords (keyword1 and keyword2). Note that keywords may repeat in the string so I must be able to get the substring between xth keyword1 and yth keyword2. Moreover, I mainly use unicode characters so the function should be charset independen.
Please help me out to handle this problem.
E.g. $string=This is their cat with a hat in the theater.
$keyword1="is"; $keyword2="the";
Task: how to get substring between 2nd occurance of "is" and 3nd occurance of "the" in the given string above.
Answer: " the cat with a hat in the "
You can use regular expressions:
$string = "This is their cat with a hat in the theater";
$regex1 = "/.*? is |^is/";
$regex2 = "/ the .*| the$/";
echo preg_replace($regex1, '', preg_replace($regex2, ' the', $string));
EDIT Here is more generic code:
function find($text, $str, $offset) {
$len = strlen($text);
$search_len = strlen($str);
$count = 0;
for ($i=0; $i<$len; ++$i) {
if (substr($text, $i, $search_len) == $str) {
if (++$count == $offset) {
return $i;
}
}
}
return -1;
}
function between($text, $word1, $offset1, $word2, $offset2) {
$start = find($text, $word1, $offset1);
$end = find($text, $word2, $offset2);
if ($start != -1 && $end != -1) {
return substr($text, $start + strlen($word1), $end-$start-strlen($word2));
} else {
return '';
}
}
$string = "This is their cat with a hat in the theater";
echo between($string, 'is', 2, 'the', 3);
echo between($string, 'at', 1, 'at', 3);
Combination of following two functions work for any string including unicode characters:
//Gets the position of a given substring with its offset;
function strposOffset($string, $search, $offset)
{
/*** explode the string ***/
$arr = explode($search, $string);
/*** check the search is not out of bounds ***/
switch( $offset )
{
case $offset == 0:
return false;
break;
case $offset > max(array_keys($arr)):
return false;
break;
default:
return mb_strlen(implode($search, array_slice($arr, 0, $offset)), "utf-8");
}
} //Source: www.phpro.org
//Extracts a substring between given two given substrings with their offsets.
function extractMiddleSubstr($string, $substr1, $offset1, $substr2, $offset2){
$strlen_substr1 = mb_strlen($substr1, "utf-8"); //length of substr1;
$strpos_substr1 = strposOffset($string, $substr1, $offset1); //position of substr1;
$strpos_substr2 = strposOffset($string, $substr2, $offset2); //position of substr2;
if($strpos_substr1!==null && $strpos_substr2!==null && $strpos_substr1!==false && $strpos_substr2!==false){
if($strpos_substr1<=$strpos_substr2){
$strpos_substr = $strlen_substr1+$strpos_substr1; //position of substr;
$strlen_substr = $strpos_substr2-$strpos_substr; //length of substr;
$substr = mb_substr($string, $strpos_substr, $strlen_substr, "utf-8"); //substr;
$substr = trim($substr); // removes whitespaces;
return $substr;
}else{
return false;
}
}else{
return false;
}
}

How to display Currency in Indian Numbering Format in PHP?

I have a question about formatting the Rupee currency (Indian Rupee - INR).
For example, numbers here are represented as:
1
10
100
1,000
10,000
1,00,000
10,00,000
1,00,00,000
10,00,00,000
Refer Indian Numbering System
I have to do with it PHP.
I have saw this question Displaying Currency in Indian Numbering Format. But couldn't able to get it for PHP my problem.
Update:
How to use money_format() in indian currency format?
You have so many options but money_format can do the trick for you.
Example:
$amount = '100000';
setlocale(LC_MONETARY, 'en_IN');
$amount = money_format('%!i', $amount);
echo $amount;
Output:
1,00,000.00
Note:
The function money_format() is only defined if the system has strfmon capabilities. For example, Windows does not, so money_format() is undefined in Windows.
Pure PHP Implementation - Works on any system:
$amount = '10000034000';
$amount = moneyFormatIndia( $amount );
echo $amount;
function moneyFormatIndia($num) {
$explrestunits = "" ;
if(strlen($num)>3) {
$lastthree = substr($num, strlen($num)-3, strlen($num));
$restunits = substr($num, 0, strlen($num)-3); // extracts the last three digits
$restunits = (strlen($restunits)%2 == 1)?"0".$restunits:$restunits; // explodes the remaining digits in 2's formats, adds a zero in the beginning to maintain the 2's grouping.
$expunit = str_split($restunits, 2);
for($i=0; $i<sizeof($expunit); $i++) {
// creates each of the 2's group and adds a comma to the end
if($i==0) {
$explrestunits .= (int)$expunit[$i].","; // if is first value , convert into integer
} else {
$explrestunits .= $expunit[$i].",";
}
}
$thecash = $explrestunits.$lastthree;
} else {
$thecash = $num;
}
return $thecash; // writes the final format where $currency is the currency symbol.
}
$num = 1234567890.123;
$num = preg_replace("/(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/i", "$1,", $num);
echo $num;
// Input : 1234567890.123
// Output : 1,23,45,67,890.123
// Input : -1234567890.123
// Output : -1,23,45,67,890.123
echo 'Rs. '.IND_money_format(1234567890);
function IND_money_format($money){
$len = strlen($money);
$m = '';
$money = strrev($money);
for($i=0;$i<$len;$i++){
if(( $i==3 || ($i>3 && ($i-1)%2==0) )&& $i!=$len){
$m .=',';
}
$m .=$money[$i];
}
return strrev($m);
}
NOTE:: it is not tested on float values and it suitable for only Integer
The example you've linked is making use of the ICU libraries which are available with PHP in the intl Extension­Docs:
$fmt = new NumberFormatter($locale = 'en_IN', NumberFormatter::CURRENCY);
echo $fmt->format(10000000000.1234)."\n"; # Rs 10,00,00,00,000.12
Or maybe better fitting in your case:
$fmt = new NumberFormatter($locale = 'en_IN', NumberFormatter::DECIMAL);
echo $fmt->format(10000000000)."\n"; # 10,00,00,00,000
Simply use below function to format in INR.
function amount_inr_format($amount) {
$fmt = new \NumberFormatter($locale = 'en_IN', NumberFormatter::DECIMAL);
return $fmt->format($amount);
}
Check this code, it works 100% for Indian Rupees format with decimal format.
You can use numbers like :
123456.789
123.456
123.4
123
and 1,2,3,4,5,6,7,8,9,.222
function moneyFormatIndia($num){
$explrestunits = "" ;
$num = preg_replace('/,+/', '', $num);
$words = explode(".", $num);
$des = "00";
if(count($words)<=2){
$num=$words[0];
if(count($words)>=2){$des=$words[1];}
if(strlen($des)<2){$des="$des";}else{$des=substr($des,0,2);}
}
if(strlen($num)>3){
$lastthree = substr($num, strlen($num)-3, strlen($num));
$restunits = substr($num, 0, strlen($num)-3); // extracts the last three digits
$restunits = (strlen($restunits)%2 == 1)?"0".$restunits:$restunits; // explodes the remaining digits in 2's formats, adds a zero in the beginning to maintain the 2's grouping.
$expunit = str_split($restunits, 2);
for($i=0; $i<sizeof($expunit); $i++){
// creates each of the 2's group and adds a comma to the end
if($i==0)
{
$explrestunits .= (int)$expunit[$i].","; // if is first value , convert into integer
}else{
$explrestunits .= $expunit[$i].",";
}
}
$thecash = $explrestunits.$lastthree;
} else {
$thecash = $num;
}
return "$thecash.$des"; // writes the final format where $currency is the currency symbol.
}
When money_format is not available :
function format($amount): string
{
list ($number, $decimal) = explode('.', sprintf('%.2f', floatval($amount)));
$sign = $number < 0 ? '-' : '';
$number = abs($number);
for ($i = 3; $i < strlen($number); $i += 3)
{
$number = substr_replace($number, ',', -$i, 0);
}
return $sign . $number . '.' . $decimal;
}
<?php
$amount = '-100000.22222'; // output -1,00,000.22
//$amount = '0100000.22222'; // output 1,00,000.22
//$amount = '100000.22222'; // output 1,00,000.22
//$amount = '100000.'; // output 1,00,000.00
//$amount = '100000.2'; // output 1,00,000.20
//$amount = '100000.0'; // output 1,00,000.00
//$amount = '100000'; // output 1,00,000.00
echo $aaa = moneyFormatIndia($amount);
function moneyFormatIndia($amount)
{
$amount = round($amount,2);
$amountArray = explode('.', $amount);
if(count($amountArray)==1)
{
$int = $amountArray[0];
$des=00;
}
else {
$int = $amountArray[0];
$des=$amountArray[1];
}
if(strlen($des)==1)
{
$des=$des."0";
}
if($int>=0)
{
$int = numFormatIndia( $int );
$themoney = $int.".".$des;
}
else
{
$int=abs($int);
$int = numFormatIndia( $int );
$themoney= "-".$int.".".$des;
}
return $themoney;
}
function numFormatIndia($num)
{
$explrestunits = "";
if(strlen($num)>3)
{
$lastthree = substr($num, strlen($num)-3, strlen($num));
$restunits = substr($num, 0, strlen($num)-3); // extracts the last three digits
$restunits = (strlen($restunits)%2 == 1)?"0".$restunits:$restunits; // explodes the remaining digits in 2's formats, adds a zero in the beginning to maintain the 2's grouping.
$expunit = str_split($restunits, 2);
for($i=0; $i<sizeof($expunit); $i++) {
// creates each of the 2's group and adds a comma to the end
if($i==0) {
$explrestunits .= (int)$expunit[$i].","; // if is first value , convert into integer
} else {
$explrestunits .= $expunit[$i].",";
}
}
$thecash = $explrestunits.$lastthree;
} else {
$thecash = $num;
}
return $thecash; // writes the final format where $currency is the currency symbol.
}
?>
So if I'm reading that right, the Indian Numbering System separates the thousands, then every power of a hundred past that? Hmm...
Perhaps something like this?
function indian_number_format($num) {
$num = "".$num;
if( strlen($num) < 4) return $num;
$tail = substr($num,-3);
$head = substr($num,0,-3);
$head = preg_replace("/\B(?=(?:\d{2})+(?!\d))/",",",$head);
return $head.",".$tail;
}
$amount=-3000000000111.11;
$amount<0?(($sign='-').($amount*=-1)):$sign=''; //Extracting sign from given amount
$pos=strpos($amount, '.'); //Identifying the decimal point position
$amt= substr($amount, $pos-3); // Extracting last 3 digits of integer part along with fractional part
$amount= substr($amount,0, $pos-3); //removing the extracted part from amount
for(;strlen($amount);$amount=substr($amount,0,-2)) // Now loop through each 2 digits of remaining integer part
$amt=substr ($amount,-2).','.$amt; //forming Indian Currency format by appending (,) for each 2 digits
echo $sign.$amt; //Appending sign
I think this a quick and simplest solution:-
function formatToInr($number){
$number=round($number,2);
// windows is not supported money_format
if(setlocale(LC_MONETARY, 'en_IN')){
return money_format('%!'.$decimal.'n', $number);
}
else {
if(floor($number) == $number) {
$append='.00';
}else{
$append='';
}
$number = preg_replace("/(\d+?)(?=(\d\d)+(\d)(?!\d))(\.\d+)?/i", "$1,", $number);
return $number.$append;
}
}
You should check the number_format function.Here is the link
Separating thousands with commas will look like
$rupias = number_format($number, 2, ',', ',');
I have used different format parameters to money_format() for my output.
setlocale(LC_MONETARY, 'en_IN');
if (ctype_digit($amount) ) {
// is whole number
// if not required any numbers after decimal use this format
$amount = money_format('%!.0n', $amount);
}
else {
// is not whole number
$amount = money_format('%!i', $amount);
}
//$amount=10043445.7887 outputs 1,00,43,445.79
//$amount=10043445 outputs 1,00,43,445
Above Function Not working with Decimal
$amount = 10000034000.001;
$amount = moneyFormatIndia( $amount );
echo $amount;
function moneyFormatIndia($num){
$nums = explode(".",$num);
if(count($nums)>2){
return "0";
}else{
if(count($nums)==1){
$nums[1]="00";
}
$num = $nums[0];
$explrestunits = "" ;
if(strlen($num)>3){
$lastthree = substr($num, strlen($num)-3, strlen($num));
$restunits = substr($num, 0, strlen($num)-3);
$restunits = (strlen($restunits)%2 == 1)?"0".$restunits:$restunits;
$expunit = str_split($restunits, 2);
for($i=0; $i<sizeof($expunit); $i++){
if($i==0)
{
$explrestunits .= (int)$expunit[$i].",";
}else{
$explrestunits .= $expunit[$i].",";
}
}
$thecash = $explrestunits.$lastthree;
} else {
$thecash = $num;
}
return $thecash.".".$nums[1];
}
}
Answer : 10,00,00,34,000.001
It's my very own function to do the task
function bd_money($num) {
$pre = NULL; $sep = array(); $app = '00';
$s=substr($num,0,1);
if ($s=='-') {$pre= '-';$num = substr($num,1);}
$num=explode('.',$num);
if (count($num)>1) $app=$num[1];
if (strlen($num[0])<4) return $pre . $num[0] . '.' . $app;
$th=substr($num[0],-3);
$hu=substr($num[0],0,-3);
while(strlen($hu)>0){$sep[]=substr($hu,-2); $hu=substr($hu,0,-2);}
return $pre.implode(',',array_reverse($sep)).','.$th.'.'.$app;
}
It took 0.0110 Seconds per THOUSAND query while number_format took 0.001 only.
Always try to use PHP native functions only when performance is target issue.
$r=explode('.',12345601.20);
$n = $r[0];
$len = strlen($n); //lenght of the no
$num = substr($n,$len-3,3); //get the last 3 digits
$n = $n/1000; //omit the last 3 digits already stored in $num
while($n > 0) //loop the process - further get digits 2 by 2
{
$len = strlen($n);
$num = substr($n,$len-2,2).",".$num;
$n = round($n/100);
}
echo "Rs.".$num.'.'.$r[1];
If you dont want to use any inbuilt function in my case i was doing on iis server so was unable to use one the function in php so did this
$num = -21324322.23;
moneyFormatIndiaPHP($num);
function moneyFormatIndiaPHP($num){
//converting it to string
$numToString = (string)$num;
//take care of decimal values
$change = explode('.', $numToString);
//taking care of minus sign
$checkifminus = explode('-', $change[0]);
//if minus then change the value as per
$change[0] = (count($checkifminus) > 1)? $checkifminus[1] : $checkifminus[0];
//store the minus sign for further
$min_sgn = '';
$min_sgn = (count($checkifminus) > 1)?'-':'';
//catch the last three
$lastThree = substr($change[0], strlen($change[0])-3);
//catch the other three
$ExlastThree = substr($change[0], 0 ,strlen($change[0])-3);
//check whethr empty
if($ExlastThree != '')
$lastThree = ',' . $lastThree;
//replace through regex
$res = preg_replace("/\B(?=(\d{2})+(?!\d))/",",",$ExlastThree);
//main container num
$lst = '';
if(isset($change[1]) == ''){
$lst = $min_sgn.$res.$lastThree;
}else{
$lst = $min_sgn.$res.$lastThree.".".$change[1];
}
//special case if equals to 2 then
if(strlen($change[0]) === 2){
$lst = str_replace(",","",$lst);
}
return $lst;
}
This for both integer and float values
function indian_money_format($number)
{
if(strstr($number,"-"))
{
$number = str_replace("-","",$number);
$negative = "-";
}
$split_number = #explode(".",$number);
$rupee = $split_number[0];
$paise = #$split_number[1];
if(#strlen($rupee)>3)
{
$hundreds = substr($rupee,strlen($rupee)-3);
$thousands_in_reverse = strrev(substr($rupee,0,strlen($rupee)-3));
$thousands = '';
for($i=0; $i<(strlen($thousands_in_reverse)); $i=$i+2)
{
$thousands .= $thousands_in_reverse[$i].$thousands_in_reverse[$i+1].",";
}
$thousands = strrev(trim($thousands,","));
$formatted_rupee = $thousands.",".$hundreds;
}
else
{
$formatted_rupee = $rupee;
}
if((int)$paise>0)
{
$formatted_paise = ".".substr($paise,0,2);
}else{
$formatted_paise = '.00';
}
return $negative.$formatted_rupee.$formatted_paise;
}
Use this function:
function addCommaToRs($amt, &$ret, $dec='', $sign=''){
if(preg_match("/-/",$amt)){
$amts=explode('-',$amt);
$amt=$amts['1'];
static $sign='-';
}
if(preg_match("/\./",$amt)){
$amts=explode('.',$amt);
$amt=$amts['0'];
$l=strlen($amt);
static $dec;
$dec=$amts['1'];
} else {
$l=strlen($amt);
}
if($l>3){
if($l%2==0){
$ret.= substr($amt,0,1);
$ret.= ",";
addCommaToRs(substr($amt,1,$l),$ret,$dec);
} else{
$ret.=substr($amt,0,2);
$ret.= ",";
addCommaToRs(substr($amt,2,$l),$ret,$dec);
}
} else {
$ret.= $amt;
if($dec) $ret.=".".$dec;
}
return $sign.$ret;
}
Call it like this:
$amt = '';
echo addCommaToRs(123456789.123,&$amt,0);
This will return 12,34,567.123.
<?php
function moneyFormatIndia($num)
{
//$num=123456789.00;
$result='';
$sum=explode('.',$num);
$after_dec=$sum[1];
$before_dec=$sum[0];
$result='.'.$after_dec;
$num=$before_dec;
$len=strlen($num);
if($len<=3)
{
$result=$num.$result;
}
else
{
if($len<=5)
{
$result='Rs '.substr($num, 0,$len-3).','.substr($num,$len-3).$result;
return $result;
}
else
{
$ls=strlen($num);
$result=substr($num, $ls-5,2).','.substr($num, $ls-3).$result;
$num=substr($num, 0,$ls-5);
while(strlen($num)!=0)
{
$result=','.$result;
$ls=strlen($num);
if($ls<=2)
{
$result='Rs. '.$num.$result;
return $result;
}
else
{
$result=substr($num, $ls-2).$result;
$num=substr($num, 0,$ls-2);
}
}
}
}
}
?>
heres is simple thing u can do ,
float amount = 100000;
NumberFormat formatter = NumberFormat.getCurrencyInstance(new Locale("en", "IN"));
String moneyString = formatter.format(amount);
System.out.println(moneyString);
The output will be , Rs.100,000.00 .
declare #Price decimal(26,7)
Set #Price=1234456677
select FORMAT(#Price, 'c', 'en-In')
Result:
1,23,44,56,677.00

PHP implementation for an URL shortening algorithm

I found Marcel Jackwerth's response to How to code a URL shortener? to be a good answer for the problem, however my question is how it'll look in PHP? Here's Marcel's answer:
You need a Bijective Function f (there must be no x1 != x2, that will make f(x1) = f(x2); and for every y you will find a x so that f(x)=y). This is necessary so that you can find a inverse function g('abc') = 123 for your f(123)='abc' function.
I would continue your "convert number to string" approach (however you will realize that your proposed algorithm fails if your id is a prime and greater than 52).
How to convert the id to a shortened url:
Think of an alphabet you want to use. In your case that's [a-zA-Z0-9]. It contains 62 letters.
Take the auto-generated unique numerical key (auto-incremented id): for example 125 (a decimal number)
Now you have to convert the 125 (base 10) to X (base 62). This will then be {2}{1} (2×62+1=125).
Now map the symbols {2} and {1} to your alphabet. Say {0} = 'a', {25} = 'z' and so on. We will have {2} = 'c' and {1} = 'b'. So '/cb' will be your shortened url.
How to resolve a shortened url abc to the initial id:
If you want to do this in reverse, it's not quite diffcult. 'e9a' will be resolved to "4th,61st,0th letter in alphabet" = {4}{61}{0}, which is 4×62×62 + 61×62 + 0 = 19158. You will then just have to find your database-record with id 19158.
function convert($src, $srcAlphabet, $dstAlphabet) {
$srcBase = strlen($srcAlphabet);
$dstBase = strlen($dstAlphabet);
$wet = $src;
$val = 0;
$mlt = 1;
while ($l = strlen($wet)) {
$digit = $wet[$l - 1];
$val += $mlt * strpos($srcAlphabet, $digit);
$wet = substr($wet, 0, $l - 1);
$mlt *= $srcBase;
}
$wet = $val;
$dst = '';
while ($wet >= $dstBase) {
$digitVal = $wet % $dstBase;
$digit = $dstAlphabet[$digitVal];
$dst = $digit . $dst;
$wet /= $dstBase;
}
$digit = $dstAlphabet[$wet];
$dst = $digit . $dst;
return $dst;
}
// prints cb
print convert('125', '0123456789', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789');
// prints 19158
print convert('e9a', 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789', '0123456789');
I like this PHP function which allows you to customise the alphabet (and remove confusing 0/O's etc.)
// From http://snipplr.com/view/22246/base62-encode--decode/
private function base_encode($val, $base=62, $chars='0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ') {
$str = '';
do {
$i = fmod($val, $base);
$str = $chars[$i] . $str;
$val = ($val - $i) / $base;
} while($val > 0);
return $str;
}
Follow the URL to find the reverse 'decode' function too.
The main problem with Marcel's solution is that it uses a zero digit as a placeholder. By converting between bases, inevitably the numeral chosen to represent 0 can't appear at the front of the converted number.
For example, if you convert base 10 integers to base 4 using "ABCD" using the provided mechanism, there is no way to obtain output that starts with the letter "A", since that represents a zero in the new base and won't prefix the number. You might expect 5 to be "AA", but instead, it is "BA". There is no way to coerce that algorithm into producing "AA", because it would be like writing "00" in decimal, which has the same value as "0".
Here's an alternate solution in PHP that uses the entire gamut:
function encode($n, $alphabet = 'ABCD') {
$output = '';
if($n == 0) {
$output = $alphabet[0];
}
else {
$digits = floor(log($n, strlen($alphabet))) + 1;
for($z = 0; $z < $digits; $z++) {
$digit = $n % 4;
$output = $alphabet[$digit] . $output;
$n = floor($n / 4) - 1;
}
}
return $output;
}
function decode($code, $alphabet = 'ABCD') {
$n = 0;
$code = str_split($code);
$unit = 1;
while($letter = array_pop($code)) {
$n += (strpos($alphabet, $letter) + 1) * $unit;
$unit = $unit * strlen($alphabet);
}
return $n - 1;
}
echo encode(25); // should output "ABB"
echo decode('ABB'); // should output 25
Change/pass the second parameter to a list of characters to use instead of the short 4-character dictionary of "ABCD".
all you need to do is convert between different base systems base 10 to base 62
https://github.com/infinitas/infinitas/blob/dev/core/short_urls/models/short_url.php

Encoding like base36 including uppercase

I am using base36 to shorten URLs. I have an id of a blog entry and convert that id to base36 to make it smaller. Base36 only includes lowercase letters. How can I include uppercase letters? If I use base64_encode it actually makes the string longer.
you can find examples of source-code to create short-urls containing letters (both lower and upper case) and number on those two articles, for instance :
Create short IDs with PHP - Like Youtube or TinyURL
Building a URL Shortener
Here is the portion of code used in that second article (quoting) :
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$n = 300;
$converted = "";
while ($n > 0) {
$converted = substr($codeset, ($n % $base), 1) . $converted;
$n = floor($n/$base);
}
echo $converted; // 4Q
And you can pretty easily encapsulate this in a function -- only thing to consider is that $n is to be received as a parameter :
function shorten($n) {
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$converted = "";
while ($n > 0) {
$converted = substr($codeset, ($n % $base), 1) . $converted;
$n = floor($n/$base);
}
return $converted;
}
And calling it this way :
$id = 123456;
$url = shorten($id);
var_dump($url);
You get :
string 'w7e' (length=3)
(You can also add some other characters, if needed -- depending on what you want to get in your URLs)
Edit after the comment :
Reading through the second article (from which I got the shortening code), you'll find the code that does the un-shortening.
Encapsulating that code in a function shouldn't be that hard, and might get you something like this :
function unshorten($converted) {
$codeset = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
$base = strlen($codeset);
$c = 0;
for ($i = strlen($converted); $i; $i--) {
$c += strpos($codeset, substr($converted, (-1 * ( $i - strlen($converted) )),1))
* pow($base,$i-1);
}
return $c;
}
And calling it with a shortened-url :
$back_to_id = unshorten('w7e');
var_dump($back_to_id);
Will get you :
int 123456
function dec2any( $num, $base=62, $index=false ) {
// Parameters:
// $num - your decimal integer
// $base - base to which you wish to convert $num (leave it 0 if you are providing $index or omit if you're using default (62))
// $index - if you wish to use the default list of digits (0-1a-zA-Z), omit this option, otherwise provide a string (ex.: "zyxwvu")
if (! $base ) {
$base = strlen( $index );
} else if (! $index ) {
$index = substr( "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" ,0 ,$base );
}
$out = "";
for ( $t = floor( log10( $num ) / log10( $base ) ); $t >= 0; $t-- ) {
$a = floor( $num / pow( $base, $t ) );
$out = $out . substr( $index, $a, 1 );
$num = $num - ( $a * pow( $base, $t ) );
}
return $out;
}
Shamelessly borrowed from a commenter on PHP's base_convert() page (base_convert() only works up to base 32).

Categories