Hashing from a public key to a bitcoin address in php - php

I am trying to follow the instructions required to turn a 65 byte public key into a bitcoin address using php. The instructions are quite explicit. Can anyone help me with the practicality of doing that in php?
Instructions are
1 - Take the corresponding public key generated with it (65 bytes, 1 byte 0x04, 32 bytes corresponding to X coordinate, 32 bytes corresponding to Y coordinate)
0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6
2 - Perform SHA-256 hashing on the public key
600FFE422B4E00731A59557A5CCA46CC183944191006324A447BDB2D98D4B408
3 - Perform RIPEMD-160 hashing on the result of SHA-256
010966776006953D5567439E5E39F86A0D273BEE
4 - Add version byte in front of RIPEMD-160 hash (0x00 for Main Network)
00010966776006953D5567439E5E39F86A0D273BEE
5 - Perform SHA-256 hash on the extended RIPEMD-160 result
445C7A8007A93D8733188288BB320A8FE2DEBD2AE1B47F0F50BC10BAE845C094
6 - Perform SHA-256 hash on the result of the previous SHA-256 hash
D61967F63C7DD183914A4AE452C9F6AD5D462CE3D277798075B107615C1A8A30
7 - Take the first 4 bytes of the second SHA-256 hash. This is the address checksum
D61967F6
8 - Add the 4 checksum bytes from point 7 at the end of extended RIPEMD-160 hash from point 4. This is the 25-byte binary Bitcoin Address.
00010966776006953D5567439E5E39F86A0D273BEED61967F6
9 - Convert the result from a byte string into a base58 string using Base58Check encoding. This is the most commonly used Bitcoin Address format
16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM
My first attempt is
// step 1
$publickey='0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6';
$step1=$publickey;
echo "step1 ".$publickey."<br>";
// step 2
$step2=hash("sha256",$step1);
echo "step2 ".$step2."<br>";
// step 3
$step3=hash('ripemd160',$step2);
echo "step3 ".$step3."<br>";
// step 4
$step4="00".$step3;
echo "step4 ".$step4."<br>";
// step 5
$step5=hash("sha256",$step4);
echo "step5 ".$step5."<br>";
// step 6
$step6=hash("sha256",$step5);
echo "step6 ".$step6."<br>";
// step 7
$checksum=substr($step6,0,8);
echo "step7 ".$checksum."<br>";
// step 8
$step8=$step4.$checksum;
echo "step8 ".$step8."<br>";
//step 9
$step9=base58_encode($step8);
echo "step9 ".$step9."<br><br>";
This fails at the first step. Any help appreciated.
This is the output
step1 0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6
step2 32511e82d56dcea68eb774094e25bab0f8bdd9bc1eca1ceeda38c7a43aceddce
step3 7528c664cdc34c5ce809778eb688d32f89a538c0
step4 007528c664cdc34c5ce809778eb688d32f89a538c0
step5 86e76f4ff0bf0387339ac70a552e0fed615f7def34cc4809df1429e243f6c1fa
step6 b885b7225b370e7ff27ee0afb4f89b52b8675d5dc342d63de3abe7535f86cadb
step7 b885b722
step8 007528c664cdc34c5ce809778eb688d32f89a538c0b885b722
step9 1
Base58 function is
function base58_encode($input)
{
$alphabet = '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ';
$base_count = strval(strlen($alphabet));
$encoded = '';
while (floatval($input) >= floatval($base_count))
{
$div = bcdiv($input, $base_count);
$mod = bcmod($input, $base_count);
$encoded = substr($alphabet, intval($mod), 1) . $encoded;
$input = $div;
}
if (floatval($input) > 0)
{
$encoded = substr($alphabet, intval($input), 1) . $encoded;
}
return($encoded);
}

Solution below with thanks to Sammitch for spotting syntax and providing the base conversions.
<?php
// step 1
$publickey='0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6';
$step1=hexStringToByteString($publickey);
echo "step1 ".$publickey."<br>";
// step 2
$step2=hash("sha256",$step1);
echo "step2 ".$step2."<br>";
// step 3
$step3=hash('ripemd160',hexStringToByteString($step2));
echo "step3 ".$step3."<br>";
// step 4
$step4="00".$step3;
echo "step4 ".$step4."<br>";
// step 5
$step5=hash("sha256",hexStringToByteString($step4));
echo "step5 ".$step5."<br>";
// step 6
$step6=hash("sha256",hexStringToByteString($step5));
echo "step6 ".$step6."<br>";
// step 7
$checksum=substr($step6,0,8);
echo "step7 ".$checksum."<br>";
// step 8
$step8=$step4.$checksum;
echo "step8 ".$step8."<br>";
// step 9
// base conversion is from hex to base58 via decimal.
// Leading hex zero converts to 1 in base58 but it is dropped
// in the intermediate decimal stage. Simply added back manually.
$step9="1".bc_base58_encode(bc_hexdec($step8));
echo "step9 ".$step9."<br><br>";
?>
hash requires a byte string not a hex string. hexStringToByteString is
function hexStringToByteString($hexString){
$len=strlen($hexString);
$byteString="";
for ($i=0;$i<$len;$i=$i+2){
$charnum=hexdec(substr($hexString,$i,2));
$byteString.=chr($charnum);
}
return $byteString;
}
base conversion (thanks to Sammitch - amended to use Bitcoin base58)
// BCmath version for huge numbers
function bc_arb_encode($num, $basestr) {
if( ! function_exists('bcadd') ) {
Throw new Exception('You need the BCmath extension.');
}
$base = strlen($basestr);
$rep = '';
while( true ){
if( strlen($num) < 2 ) {
if( intval($num) <= 0 ) {
break;
}
}
$rem = bcmod($num, $base);
$rep = $basestr[intval($rem)] . $rep;
$num = bcdiv(bcsub($num, $rem), $base);
}
return $rep;
}
function bc_arb_decode($num, $basestr) {
if( ! function_exists('bcadd') ) {
Throw new Exception('You need the BCmath extension.');
}
$base = strlen($basestr);
$dec = '0';
$num_arr = str_split((string)$num);
$cnt = strlen($num);
for($i=0; $i < $cnt; $i++) {
$pos = strpos($basestr, $num_arr[$i]);
if( $pos === false ) {
Throw new Exception(sprintf('Unknown character %s at offset %d', $num_arr[$i], $i));
}
$dec = bcadd(bcmul($dec, $base), $pos);
}
return $dec;
}
// base 58 alias
function bc_base58_encode($num) {
return bc_arb_encode($num, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
}
function bc_base58_decode($num) {
return bc_arb_decode($num, '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz');
}
//hexdec with BCmath
function bc_hexdec($num) {
return bc_arb_decode(strtolower($num), '0123456789abcdef');
}
function bc_dechex($num) {
return bc_arb_encode($num, '0123456789abcdef');
}
final output
step1 0450863AD64A87AE8A2FE83C1AF1A8403CB53F53E486D8511DAD8A04887E5B23522CD470243453A299FA9E77237716103ABC11A1DF38855ED6F2EE187E9C582BA6
step2 600ffe422b4e00731a59557a5cca46cc183944191006324a447bdb2d98d4b408
step3 010966776006953d5567439e5e39f86a0d273bee
step4 00010966776006953d5567439e5e39f86a0d273bee
step5 445c7a8007a93d8733188288bb320a8fe2debd2ae1b47f0f50bc10bae845c094
step6 d61967f63c7dd183914a4ae452c9f6ad5d462ce3d277798075b107615c1a8a30
step7 d61967f6
step8 00010966776006953d5567439e5e39f86a0d273beed61967f6
step9 16UwLL9Risc3QfPqBUvKofHmBQ7wMtjvM

Look carefully at your variable names. $publickey is not the same as $publicKey - note capitalisation.

Your problems are as follow:
Variable names $publickey and $publicKey are not equivalent.
$checksum=substr($step6,0,4); should be $checksum=substr($step6,0,8); because you need two hex chars to represent one byte.
$step8=$step4+$checksum; should be $step8=$step4.$checksum;
Also, I don't know where your base58_encode() function comes from, but I hope it uses BCmath, because the number represented by 00ba084d3f143f2896809d3f1d7dffed472b39d8de7a39cf51 [step 8's result] is too large for PHP to handle internally.
edit
I'm super bored at work today, here's my conversion code with bonus BCmath for ginormous [say, 58-digit?] numbers.
<?php
// original arbitrary encode function
function arb_encode($num, $basestr) {
$base = strlen($basestr);
$rep = '';
while($num > 0) {
$rem = $num % $base;
$rep = $basestr[$rem] . $rep;
$num = ($num - $rem) / $base;
}
return $rep;
}
function arb_decode($num, $basestr) {
$base = strlen($basestr);
$dec = 0;
$num_arr = str_split((string)$num);
$cnt = strlen($num);
for($i=0; $i < $cnt; $i++) {
$pos = strpos($basestr, $num_arr[$i]);
if( $pos === false ) {
Throw new Exception(sprintf('Unknown character %s at offset %d', $num_arr[$i], $i));
}
$dec = ($dec * $base) + $pos;
}
return $dec;
}
// BCmath version for huge numbers
function bc_arb_encode($num, $basestr) {
if( ! function_exists('bcadd') ) {
Throw new Exception('You need the BCmath extension.');
}
$base = strlen($basestr);
$rep = '';
while( true ){
if( strlen($num) < 2 ) {
if( intval($num) <= 0 ) { break; }
}
$rem = bcmod($num, $base);
$rep = $basestr[intval($rem)] . $rep;
$num = bcdiv(bcsub($num, $rem), $base);
}
return $rep;
}
function bc_arb_decode($num, $basestr) {
if( ! function_exists('bcadd') ) {
Throw new Exception('You need the BCmath extension.');
}
$base = strlen($basestr);
$dec = '0';
$num_arr = str_split((string)$num);
$cnt = strlen($num);
for($i=0; $i < $cnt; $i++) {
$pos = strpos($basestr, $num_arr[$i]);
if( $pos === false ) {
Throw new Exception(sprintf('Unknown character %s at offset %d', $num_arr[$i], $i));
}
$dec = bcadd(bcmul($dec, $base), $pos);
}
return $dec;
}
// base 58 alias
function bc_base58_encode($num) {
return bc_arb_encode($num, '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ');
}
function bc_base58_decode($num) {
return bc_arb_decode($num, '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ');
}
//hexdec with BCmath
function bc_hexdec($num) {
return bc_arb_decode(strtolower($num), '0123456789abcdef');
}
function bc_dechex($num) {
return bc_arb_encode($num, '0123456789abcdef');
}
// example
$orig = '00ba084d3f143f2896809d3f1d7dffed472b39d8de7a39cf51';
$bten = bc_hexdec($orig);
$base58 = bc_base58_encode($bten);
$backten = bc_base58_decode($base58);
$back = bc_dechex($backten);
echo "Orig: " . $orig . "\n";
echo "bten: " . $bten . "\n";
echo "58: " . $base58 . "\n";
echo "ag10: " . $backten . "\n";
echo "Back: " . $back . "\n";
edit2
Don't use base_convert() for numbers this large, it appears to be unreliable. I was writing the necessary bc_arb_decode() counterparts and found that the input and output were differing using base_convert() versus arb_convert($num, '0123456789abcdef'); and after comparing the results with Wolfram Alpha it seems that PHP is incorrectly converting the number.
Hex: 00ba084d3f143f2896809d3f1d7dffed472b39d8de7a39cf51
PHP's decode: 4561501878697786606686086062428080084446806606846864824262
Mine: 4561501878697784703577561586669353227270827349968709865297
Wolfram Alpha: 4561501878697784703577561586669353227270827349968709865297
You can see that PHP is way off. [1.9E42 aka 1.9 quintillion septillion] I've updated my code to include the arb_decode() functions which appear to do things correctly.

Very Important!
Replace this: '123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ'
With this: '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
Using the wrong code here would cause Bitcoin transactions to fail or worse, cause coins to disappear to a phantom wallet where they can never be retrieved.
I am not a developer but I confirmed the correction. The Base58 symbol chart is here, https://en.bitcoin.it/wiki/Base58Check_encoding
I checked my work here http://brainwallet.org/
Enter the passphrase: "test address" without the quotes.
The public key is then: 047969a753f71135d4c792f384e546cd508514024b4ee40d12a014019b77d1b292763dfb8a108cf7a7119f80ca4a06e81b92464f5d8a7544d52cd2e641023a96d7
Your address result : 1gBG1mbVtyNTgGZhggJ21A6mnjbNtqPCSr
My result: 1Gch1MBvUZotGhzHGGj21b6MNKBoURpdsS
brainwallet.org result: 1Gch1MBvUZotGhzHGGj21b6MNKBoURpdsS
I hope this saves someone from a time consuming or costly error.

Related

SQL and PHP: Is it possible to convert a string to binary without using SQL function [duplicate]

I got the problem when convert between this 2 type in PHP. This is the code I searched in google
function strToHex($string){
$hex='';
for ($i=0; $i < strlen($string); $i++){
$hex .= dechex(ord($string[$i]));
}
return $hex;
}
function hexToStr($hex){
$string='';
for ($i=0; $i < strlen($hex)-1; $i+=2){
$string .= chr(hexdec($hex[$i].$hex[$i+1]));
}
return $string;
}
I check it and found out this when I use XOR to encrypt.
I have the string "this is the test", after XOR with a key, I have the result in string ↕↑↔§P↔§P ♫§T↕§↕. After that, I tried to convert it to hex by function strToHex() and I got these 12181d15501d15500e15541215712. Then, I tested with the function hexToStr() and I have ↕↑↔§P↔§P♫§T↕§q. So, what should I do to solve this problem? Why does it wrong when I convert this 2 style value?
For people that end up here and are just looking for the hex representation of a (binary) string.
bin2hex("that's all you need");
# 74686174277320616c6c20796f75206e656564
hex2bin('74686174277320616c6c20796f75206e656564');
# that's all you need
Doc: bin2hex, hex2bin.
For any char with ord($char) < 16 you get a HEX back which is only 1 long. You forgot to add 0 padding.
This should solve it:
<?php
function strToHex($string){
$hex = '';
for ($i=0; $i<strlen($string); $i++){
$ord = ord($string[$i]);
$hexCode = dechex($ord);
$hex .= substr('0'.$hexCode, -2);
}
return strToUpper($hex);
}
function hexToStr($hex){
$string='';
for ($i=0; $i < strlen($hex)-1; $i+=2){
$string .= chr(hexdec($hex[$i].$hex[$i+1]));
}
return $string;
}
// Tests
header('Content-Type: text/plain');
function test($expected, $actual, $success) {
if($expected !== $actual) {
echo "Expected: '$expected'\n";
echo "Actual: '$actual'\n";
echo "\n";
$success = false;
}
return $success;
}
$success = true;
$success = test('00', strToHex(hexToStr('00')), $success);
$success = test('FF', strToHex(hexToStr('FF')), $success);
$success = test('000102FF', strToHex(hexToStr('000102FF')), $success);
$success = test('↕↑↔§P↔§P ♫§T↕§↕', hexToStr(strToHex('↕↑↔§P↔§P ♫§T↕§↕')), $success);
echo $success ? "Success" : "\nFailed";
PHP :
string to hex:
implode(unpack("H*", $string));
hex to string:
pack("H*", $hex);
Here's what I use:
function strhex($string) {
$hexstr = unpack('H*', $string);
return array_shift($hexstr);
}
function hexToStr($hex){
// Remove spaces if the hex string has spaces
$hex = str_replace(' ', '', $hex);
return hex2bin($hex);
}
// Test it
$hex = "53 44 43 30 30 32 30 30 30 31 37 33";
echo hexToStr($hex); // SDC002000173
/**
* Test Hex To string with PHP UNIT
* #param string $value
* #return
*/
public function testHexToString()
{
$string = 'SDC002000173';
$hex = "53 44 43 30 30 32 30 30 30 31 37 33";
$result = hexToStr($hex);
$this->assertEquals($result,$string);
}
Using #bill-shirley answer with a little addition
function str_to_hex($string) {
$hexstr = unpack('H*', $string);
return array_shift($hexstr);
}
function hex_to_str($string) {
return hex2bin("$string");
}
Usage:
$str = "Go placidly amidst the noise";
$hexstr = str_to_hex($str);// 476f20706c616369646c7920616d6964737420746865206e6f697365
$strstr = hex_to_str($str);// Go placidly amidst the noise
You can try the following code to convert the image to hex string
<?php
$image = 'sample.bmp';
$file = fopen($image, 'r') or die("Could not open $image");
while ($file && !feof($file)){
$chunk = fread($file, 1000000); # You can affect performance altering
this number. YMMV.
# This loop will be dog-slow, almost for sure...
# You could snag two or three bytes and shift/add them,
# but at 4 bytes, you violate the 7fffffff limit of dechex...
# You could maybe write a better dechex that would accept multiple bytes
# and use substr... Maybe.
for ($byte = 0; $byte < strlen($chunk); $byte++)){
echo dechex(ord($chunk[$byte]));
}
}
?>
I only have half the answer, but I hope that it is useful as it adds unicode (utf-8) support
/**
* hexadecimal to unicode character
* #param string $hex
* #return string
*/
function hex2uni($hex) {
$dec = hexdec($hex);
if($dec < 128) {
return chr($dec);
}
if($dec < 2048) {
$utf = chr(192 + (($dec - ($dec % 64)) / 64));
} else {
$utf = chr(224 + (($dec - ($dec % 4096)) / 4096));
$utf .= chr(128 + ((($dec % 4096) - ($dec % 64)) / 64));
}
return $utf . chr(128 + ($dec % 64));
}
To string
var_dump(hex2uni('e641'));
Based on: http://www.php.net/manual/en/function.chr.php#Hcom55978

How to convert MSB - LSB Format handling in PHP

I want to convert MSB to LSB.
Input Data = 764491139 (MSB) , Correct result = 2201325869 (LSB)
My coding :
$value = "764491139";
echo bindec(strrev(decbin($value))); //Convert to LSB
Result : 812327533; //Correct is 2201325869
but result is incorrect.
Could you help me?
A simple search in Google founds this snippet from the PHP manual
function uInt64($i, $endianness=false) {
$f = is_int($i) ? "pack" : "unpack";
if ($endianness === true) { // big-endian
$i = $f("J", $i);
}
else if ($endianness === false) { // little-endian
$i = $f("P", $i);
}
else if ($endianness === null) { // machine byte order
$i = $f("Q", $i);
}
return is_array($i) ? $i[1] : $i;
}
http://php.net/manual/en/function.unpack.php#119403

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