PHP Convert money to string - php

I have a piece of code that is creating a progress bar on my page to show how much a user has spent out of an allotted amount.
$roof = 75
$total = 275.00 this works fine
$total = 1,202.00 this breaks because of the comma
<div class="progress">
<div class="progress-bar <?php if($total > $roof) { echo 'progress-bar-danger'; } ?>" role="progressbar" aria-valuenow="<?php echo $total ; ?>" aria-valuemin="0" aria-valuemax="<?php echo $roof ; ?>" style="width: <?php echo $total ; ?>%;">$<?php echo $total ; ?> of $<?php echo $roof ; ?> cap</div>
</div>
How can I get this to work correctly with higher dollar amounts that include these commas?

You should not do any logic with an already formatted string. Keep $total a clean integer or float and only add the decimal point and thousands separator on output.
PHP has the helpful number_format() function for this.
<?php
// Central function that is responsible to format currency
// values for display
function formatMoney($sum) {
$formattedString = number_format($sum, 2, "." , ",");
return $formattedString
}
$roof = 1000;
$total = 1202;
?>
<div>
<?php
// Format the number just for output:
echo formatMoney($total);
// $total remains a number:
if ($total > $roof) {
echo "warning";
}
?>
</div>
With this pattern, your business logic will be independent of how a currency value is displayed to the user. Otherwise you will be in trouble if you decide to change the number formatting later (e.g. when you add localization).
If you have no access to the unformatted value of $total you need to parse it back into a number:
// Remove thousands separator and parse into a Float value:
$total = floatval(str_replace(",", "", $formattedTotal));
Now you can use it as a number again and use it for things like $total > $roof.

Use echo number_format($total), you don't have to format it on your own.

One way:
$total = str_replace(',', '', $total);

Related

How to reverse a PHP while loop including incremental numbers starting with last number of varying length?

I have a working php code which made a numerical loop counting up from 1.
<?php
$i = 0; // define number
while (have_rows('the_rows')) : row(); // begin while loop
$i++; // increase increment with each loop ?>
<div>
<?php echo $i; // outputting the number ?>
</div>
<?php
endwhile; // end while loop
?>
This code will loop through all available entries and stop after there are no more entries. It will count up for each $i output.
For example, it might look like this:
<div>1</div>
<div>2</div>
<div>3</div>
<div>4</div>
<div>5</div>
<div>6</div>
<div>7</div>
There is no pre-set number. It could be any number including 1, 5, 74, or 938 entries. The loop will continue to all entries and stops after all entries are finished.
This works fine if I start at one. But since the number is not fixed, looping backwards is a tad more difficult.
How can I reverse this loop so that first of all, it counts the numbers backwards (e.g. if there are 7 rows, it counts backwards from 7), and also to reverse the loop starting with the last row?
e.g.
<div>7</div>
<div>6</div>
<div>5</div>
<div>4</div>
<div>3</div>
<div>2</div>
<div>1</div>
If you don't know the length of the data, get the length of the data and then print it out, in reverse.
<?php
$i = 0;
while(has_rows('the_rows')):
row();
$i++;
endwhile;
while($i > 0):
?>
<div><?php echo $i--; ?></div>
<?php endwhile; ?>
Without knowing the number of rows in advance and without looping twice, you have several options.
One is to build an array and reverse it:
<?php
$i = 0;
while (have_rows('the_rows')) : row();
$i++;
$html[] = "
<div>
$i
</div>";
endwhile;
echo implode(array_reverse($html));
Or prepend each one onto an array:
<?php
$i = 0;
while (have_rows('the_rows')) : row();
$i++;
$html = "
<div>
$i
</div>";
array_unshift($data, $html);
endwhile;
echo implode($data);
Or prepend the new string to the existing one:
<?php
$i = 0;
$html = "";
while (have_rows('the_rows')) : row();
$i++;
$html = "
<div>
$i
</div>$html";
endwhile;
echo $html;
However this looks like WordPress or something, so I would think there would be a way to get the number of rows before the loop.

Cant get if sentence to work?

I am using a foreach loop to run through an array. With it I have $q which iterates with a value of 1 on every loop run. And when the value reaches 1/3 of the total, it is to echo out new div, in order to make several columns.
But I cant seem to find the error.
$i = 0;
$count = count($segments->getItems());
$countdiv = $count / 3;
$countdiv = number_format((float)$countdiv,0,',','');
$q = 0;
foreach($segments->getItems() as $segment)
{
$q++;
$allusers = 0;
if($segment->getName() === 'All Users') {
$allusers = "checked";
}
?>
<label class="custom-control custom-checkbox">
<input type="checkbox" name="<?php echo $segment->getName();?>" value="segments[]" class="custom-control-input" <?php echo $allusers?>>
<span class="custom-control-indicator"></span>
<span class="custom-control-description"><?php echo $segment->getName();?></span>
</label>
<?php
if($q === $countdiv)
{
?>
</div>
</div>
<div class="col-md-6">
<div class="custom-controls-stacked">
<?php
}
}
number_format() returns a formatted string of the number. So when you then compare that string to an actual number using ===, it will always be false, since a string type can never be strictly equivalent to a number type. Also, it would only work the first time around as $q is always increasing.
As #Chris Forrence recommends you can do:
if(($q % round($count / 3)) === 0)
Let's dive into that a little bit. First we divide $count by 3 since we want three columns. We can't divide on a fraction of an item, so let's round that result to a whole number. We then use that to take the modulo (%) of $q. This just says divide x / y but instead of the result, give me the remainder. So each time that $q is a multiple of $count / 3 this will return 0. So, if we test that whole calculation to see if it equals 0 then we will know when we've hit one of our column boundaries.
If you're looping over a large amount of objects and performance becomes an issue, change your $countdiv declaration to be:
$countdiv = round($count / 3)
And then the above if statement can be cut down to:
if(($q % $countdiv) === 0)

PHP Sorting Array by Time & finding the closest matches to X numbers

My goal here is not to save any items to a database, but to just display a live stream.
I am pulling an RSS feed from Huffington Post
http://www.huffingtonpost.com/section/front-page/feed
I have a WordPress array (PHP) of the most recent 50 articles from the Huff.
$rss = fetch_feed($feed_url);
I want my RSS feed to ONLY display X total unique posts per day. To make it simple, I was just going to display the post that is closest to intervals of 24 / X.
For demonstration let's go with 3. The feed would spit out posts published closest to 8, 16 (2 PM), and 24 (midnight) or (0, 8, and 16).
In PHP, how do I sort an object array by a published time variable and then find the closest post to that time? Right now I'm doing a very roundabout way that currently isn't even working.
Here's my current logic:
if(function_exists('fetch_feed')) {
$rss = fetch_feed(get_field('feed_url'));
if(!is_wp_error($rss)) : // error check
$maxitems = $rss->get_item_quantity(50); // number of items at 50
$rss_items = $rss->get_items(0, $maxitems);
endif;
// display feed items ?>
<h1><?php echo $rss->get_title(); ?></h1>
<?php
$coutner = 0;
$daily_max = 3; //how many unique feeds to display per day
$display_interval = floor(24 / $daily_max); //simple way to make even intervals
$posting_time = array(); //to store the times to post
foreach(range(0, $daily_max-1) as $i) {
$posting_time[$i] = $display_interval * $i;
}
$post_interval = 0;
$date = new DateTime();
$today = date("G"); //getting the current day's hour
$time_adjust = $today / $display_interval;
//adjust the posting times order so that its circular
while($today > $posting_time[0]){
$hold = array_pop($posting_time);
echo '<p>hold: ' . $hold;
array_unshift($posting_time,$hold);
}
$accessing = array_pop($posting_time);
?>
<dl>
<?php if($maxitems == 0){ echo '<dt>Feed not available.</dt>';}
else{
foreach ($rss_items as $item) : ?>
<?php
//as soon as the first item is newer than post time, output it & count that time slot as being filled
$rss_item_hour = $item->get_date('G');
if($rss_item_hour > $accessing){ ?>
<dt>
<a href="<?php echo $item->get_permalink(); ?>"
title="<?php echo $item->get_date('j F Y # G'); ?>">
<?php echo $item->get_title(); ?>
</a>
</dt>
<dd>
<?php echo $item->get_description(); ?>
</dd>
<p>
<?php echo $item->get_date('j F Y | G');
?>
</p>
<?php $coutner = $coutner + 1;
$accessing = array_pop($posting_time);
}
else{echo '<p>else';} ?>
<?php endforeach; ?>
</dl>
<?php }} ?>
The main error right now is that sometimes the circular shifting of while($today > $posting_time[0]){ goes on infinitely. And the loop never seems to go as planned.
I tried to build a solution based on the approach that you took and made it much simpler. There are a number of edge cases that should be considered, and I will explain those later, but I think that it will still achieve the basic goal of your app as is, but I did make some assumptions.
...
<?php
$counter = 0;
$daily_max = 3; //how many unique feeds to display per day
$display_interval = floor(24 / $daily_max); //simple way to make even intervals
$posting_time = array(); //to store the times to post
// Create a list of time intervals largest to smallest ex. [16, 8, 0]
foreach(range($daily_max-1, 0) as $i) {
$posting_time[] = $display_interval * $i;
}
?>
<dl>
<?php
if($maxitems == 0){
echo '<dt>Feed not available.</dt>';
}
else{
foreach ($rss_items as $item){
if(count ($posting_time) == 0){
break;
}
//as soon as the first item is older than the most current post time, output it & count that time slot as being filled
$rss_item_hour = $item->get_date('G');
if($rss_item_hour < $posting_time[0]){
?>
<dt>
<a href="<?php echo $item->get_permalink(); ?>"
title="<?php echo $item->get_date('j F Y # G'); ?>">
<?php echo $item->get_title(); ?>
</a>
</dt>
<dd>
<?php echo $item->get_description(); ?>
</dd>
<p>
<?php echo $item->get_date('j F Y | G'); ?>
</p>
<?php
$counter++;
array_pop($posting_time);
}
else{
// Debug message
}
}
}
?>
</dl>
...
Ok, so since I don't have access to your fetch_feed data, this is untested, but I am happy to update if there are bugs.
What this will do is pick posts that are roughly broken up by the interval that you specify, but it does not do any checking to determine how close they are to those boundaries. For example, if the latest post is at 16:01, it will be skipped in favor of the first post that comes before 16:00, which may be at say, 9:00. Then it will look for the first post that is before 8:00, which may be at 7:59, so then you will have two posts that are really close in time. Or if there are no posts between 16:00 and 8:00, the first post displayed may be at 7:30, and then the very next post, maybe at 7:28 will also get displayed (since it is now the first post available before 8).
My assumption was that you are less concerned about the exact spacing and are more interested in 'thinning' out the volume of posts a little, which this should achieve and is hopefully suitable for your application.
As I said, I am happy to help you refine it if you have something specific in mind.
Considering the time as "seconds of the day" (0 - 86400) the following lines would serve your needs (simplified example):
<?php
$postTimes = array(1,600,953,1900,23500,27600,56000,72000);
echo "Closest match is: " + findMatch(24000, $postTimes); //23500
function findMatch($needle, $haystack) {
$closest = null;
foreach ($haystack as $element) {
if ($closest === null || abs($needle - $closest) > abs($element - $needle)) {
$closest = $element;
}
}
return $closest;
}
?>
Finally you just need to implement:
getPostTimesAsSeconds($postArray); //foreach converting dates to seconds-array
and
pickPostBySecondsOfTheDay(23500); //foreach, finding the post matching the seconds of the day.
Try the below example, file_get_contents is used for this example to fetch the xml. It will fetch all the feeds from the past 8 hours. Try to use DOMDocument to handle the xml feed and Datetime to manage the time comparisons needed.
$hour_interval = 8;
$feeds = file_get_contents("http://www.huffingtonpost.com/section/front-page/feed");
$doc = new DOMDocument();
$doc->loadXML($feeds);
$items = $doc->getElementsByTagName('item');
$today = new DateTime("now",new DateTimeZone("-04:00")); // do mind the timezone it is the one set in the xml feeds so it is needed for correct time comparison
$nowTimestamp = $today->getTimestamp();
$today->modify('-'.$hour_interval.' hour');
$eightHoursBeforeTimestamp = $today->getTimestamp();
$lastEightHoursItems = [];
foreach ($items as $item) {
$pubDate = $item->getElementsByTagName('pubDate')[0]->nodeValue;
$feedDate = new DateTime($pubDate);
$feedTimestamp = $feedDate->getTimestamp();
if($feedTimestamp<=$nowTimestamp and $feedTimestamp>=$eightHoursBeforeTimestamp) {
array_push($lastEightHoursItems,$item);
}
}
$random_keys = array_rand($lastEightHoursItems,3);
$c = count($random_keys);
for($i=0;$i<$c;$i++) {
echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('title')[0]->nodeValue;
echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('link')[0]->nodeValue;
echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('description')[0]->nodeValue;
echo $lastEightHoursItems[$random_keys[$i]]->getElementsByTagName('pubDate')[0]->nodeValue;
}

Best practice to sum and format numbers to dollar values with 2 decimal points?

Here's the code I came up with, I want to:
sum price + shipping to a total
always display formatted numbers with two decimal points
have a dollar sign at the start for all three values
display an alternative "free shipping" if the shipping value is zero.
PHP:
$price0 = "356";
$ship0 = "9.9";
$tprice0 = $price0 + $ship0;
$price1 = number_format((float)$price0, 2, '.', '');
$ship1 = number_format((float)$ship0, 2, '.', '');
$tprice1 = number_format((float)$tprice0, 2, '.', '');
if($ship0 == 0) { $shipping1 = '<span class="free">Free Shipping</span>'; } else { $shipping1 = "$$ship1"; }
HTML:
Price: $<?php echo $price1; ?>
Shipping: <?php echo $shipping1; ?>
Total: $<?php echo $tprice?>
Is this the best practice? (e.g, I'm using a double dollar sign), and is there a way to make the code cleaner / shorter?
EDIT:
note that I want to have this practice for several products in the same page, so is there a way to make it shorter?
$price0b = "223";
$ship0b = "5.5";
$tprice0b = $price0b + $ship0b;
$price1b = number_format((float)$price0b, 2, '.', '');
$ship1b = number_format((float)$ship0b, 2, '.', '');
$tprice1b = number_format((float)$tprice0b, 2, '.', '');
if($ship0b == 0) { $shipping1b = '<span class="free">Free Shipping</span>'; } else { $shipping1b = "$$ship1b"; }
EDIT2:
I'm getting a class not found error with this code:
<?php
$price = "356";
$shipping = "0";
$total_price = $price0 + $ship0;
$oNumberFormatter = new NumberFormatter('en_US', NumberFormatter::PATTERN_DECIMAL, '¤ #,##0.00');
?>
<!DOCTYPE html>
<html>
<head>
</head>
<body>
Price: <?= $oNumberFormatter->format( (float) $price ); ?>
Shipping: <?= !$ship0b ? "<span class=\"free\">Free Shipping</span>" : $oNumberFormatter->format( (float) $shipping ); ?>
Total: <?= $oNumberFormatter->format( (float) $total_price ); ?>
To format a number to en_US currency format you can create a NumberFormatter object like this:
$oNumberFormatter = new NumberFormatter('en_US', NumberFormatter::CURRENCY);
In this instance prices will be formatted up to normal rules for the given locale; for instance $9.95 for 'en_US` or € 9,95 for 'nl_NL'
If you want a specific ICU Decimal format you could use:
$oNumberFormatter = new NumberFormatter('en_US', NumberFormatter::PATTERN_DECIMAL, '¤ #,##0.00');
The currency symbol (¤) in the format will automatically be converted, when echoed out, to the correct symbol specified by the locale string; so for 'en_US' the dollar symbol ($) will be used - for 'nl_NL' the Euro (€) and so on.
Either way, in your product display loop, to display the correctly formatted product price, you need only (assuming your product price here is $price) use:
<?= $oNumberFormatter->format( (float) $price); ?>
... and for your free shipping insert:
<?= !$shipping ? "<span class=\"free\">Free Shipping</span>" : $oNumberFormatter->format( (float) $shipping ); ?>
Full details on the NumberFormatter class are at: http://php.net/manual/en/class.numberformatter.php
Just an edit to answer the question in the comments
Ok, thanks. can you explain what you did with the shipping part? I
didn't understand how that format is working. also, how to insert a
dollar sign if for the time being I will use Free Shipping" : number_format((float)$shipping,
2, '.', ''); ?> ?
<?= is shorthand for <?php echo
I'm just using a ternary operator: http://php.net/manual/en/language.operators.comparison.php (scroll down the page a little) - it's just a sort of shorthand for directly assigning a value based on a sort of if (?) else (:) notation:
!$shipping equates to $shipping == false which equates to $shipping == 0 since PHP is loosely typed, so:
<?= !$shipping ? "<span class=\"free\">Free Shipping</span>" : $oNumberFormatter->format( (float) $shipping ); ?>
... is equivalent to ...
<?php
if($shipping == 0) {
echo "<span class=\"free\">Free Shipping</span>";
}
else {
echo $oNumberFormatter->format( (float) $shipping );
}
?>
And for literally outputting a $ string... just use a non-interpolated string literal. PHP has 2 types of string literal (well, 4 with heredoc syntaxes) but essentially an interpolated string is enclosed in double quotes and will parse variables.
<?php
$sWord = "BADGERS";
echo "I like $sWord";
?>
Results in I like BADGERS
Whereas a non-interpolated string (enclosed in apostrophes) will treat $ literally - it won't interpolate variables:
<?php
$sWord = "BADGERS";
echo 'I like $sWord';
?>
Results in I like $sWord literally.
So you can echo out your currency value, with a prefixed dollar sign, like so:
echo '$' . number_format( (float) $shipping, 2, '.', '');
i will most likely do this
PHP
$price = "356";
$ship = "9.9";
$tprice = $price + $ship;
HTML
Price: $<?php echo setdecimalPoint($price); ?>
<?php if($ship == 0) { ?>
<span class="free">Free Shipping</span>
<?php }else{ ?>
Shipping: <?php echo setdecimalPoint($shipping); ?>
<?php } ?>
Total: $<?php echo setdecimalPoint($tprice)?>

weird float to int when moving a var

Here's my code, fetch_data.php returns a float (an exchange rate).
The first echo will return the float that I need (0.65 something) but the second one will return an integer (1)... Why? How do I fix this?
I'm using include because file_get_contents returned PHP for some reason? I don't have a lot of PHP experience and haven't written a line in a month so I'm rusty.
<div class="callout panel">
<p><strong>We sell for:</strong> $<?php
$coin_price = include('fetch_data.php');
$doge = $coin_price;
echo $coin_price;
echo $doge;
?> per 1000</p>
</div>
fetch data
$string = strip_tags($element); //<strong>$484.66</strong>
$string = str_replace('$', '', $string);
$int = $string;
$val = $int * 1.25;
echo $val / 1000;
any help appreciated
fetch_data.php
$string = strip_tags($element); //<strong>$484.66</strong>
$string = str_replace('$', '', $string);
$int = $string;
$val = $int * 1.25;
$coin_price = $val / 1000;
other php file
<div class="callout panel">
<p><strong>We sell for:</strong>
$<?php
include('fetch_data.php');
echo $coin_price;
$doge = $coin_price;
echo $doge;
?> per 1000</p>
</div>
I modified the code a bit if you want to use include like you are currently. When a file is included, the code it contains inherits the variable scope of the line on which the include occurs. Any variables available at that line in the calling file will be available within the called file, from that point forward.
You can use
$doge = floatval($coin_price);
Check here : http://www.php.net/manual/en/function.floatval.php

Categories