Why does this function not work in php? - php

Here is my function,
It is meant to get the user level an the amount of xp needed until the next level, it works but only through levels 1 to 2, then if the required xp for level 3 is entered it fails.
The XP doubles per level, so from level 1 to 2 is 10, 2 to 3 is 20, 3 to 4 is 40 etc;
$user['xp'] is an int to explain better, here are some examples of what the function returns with different values.
$user['xp'] == 1, level 1, xpGot 1, xpNeeded 9, 10%
$user['xp'] == 5, level 1, xpGot 5, xpNeeded 5, 50%
$user['xp'] == 9, level 1, xpGot 9, xpNeeded 1, 90%
$user['xp'] == 10, level 2, xpGot 0, xpNeeded 20, 0%
$user['xp'] == 15, level 2, xpGot 5, xpNeeded 15, 25%
$user['xp'] == 29, level 2, xpGot 19, xpNeeded 1, 95%
7. $user['xp'] == 30, level 2, xpGot 0, xpNeeded 0, 0%
It fails there on after.
function calculateLevel($xpGot) {
$level = 1;
$xpNeeded = 10;
while ($xpGot >= $xpNeeded) {
$level++;
$xpGot %= $xpNeeded;
$xpNeeded *= 2;
}
if ($xpGot < $xpNeeded) {
$xp = $xpGot / $xpNeeded * 100;
echo '<p>Level: ' . $level . '</p>';
echo '<div class="displayBarWrap" title="' . $xpGot . '/' . $xpNeeded . ' XP (' . $xp . '%)">
<p>XP:</p>
<div class="displayBar"><div style="width: ' . $xp . '%;"></div></div></div>';
}
}
calculateLevel($user['xp']);

I think you want following: replace %= with -=:
$xpGot -= $xpNeeded;

Take this line
$xpGot %= $xpNeeded;
There is no difference if the $xpGot is 15 or 45, % 10 will still return 5. The modulus operator divides the left operand by the right and returns the remainder.
Changing it to -= as #scessor suggested is probably what you're looking for.

With the fix the others mentioned and a possible mistake in the echo, this works for me:
<?php
function calculateLevel($xpGot) {
$level = 1;
$xpNeeded = 10;
if ($xpGot < $xpNeeded) /* NEW! */
return;
while ($xpGot >= $xpNeeded) {
$level++;
$xpGot -= $xpNeeded; /* FIXED (other answers!) */
$xpNeeded *= 2;
}
if ($xpGot < $xpNeeded) {
$xp = $xpGot / $xpNeeded * 100;
echo '<p>Level: ' . $level . '</p>';
/* FIXED ? */
echo '<div class="displayBarWrap" title="' . $xpGot . '/' . $xpNeeded . ' XP (' . $xp . '%)"> <p>XP:</p> <div class="displayBar"><div style="width: ' . $xp . '%;"></div></div></div>';
}
}
calculateLevel($user['xp']);
?>

Related

Is there a known way to validate an Estonian business ID?

I have to validate an Estonian business ID (not a citizen ID). The ID is 9 numbers, but I think there might be a system of assigning them. For example, here is the Finnish validation with the last number as the checksum
if (preg_match('/^\d{7}-\d{1}$/', $user_input)) {
list($num, $control) = preg_split('[-]', $user_input);
// Add leading zeros if number is < 7
$num = str_pad($num, 7, 0, STR_PAD_LEFT);
$controlSum = 0;
$controlSum += (int)substr($num, 0, 1)*7;
$controlSum += (int)substr($num, 1, 1)*9;
$controlSum += (int)substr($num, 2, 1)*10;
$controlSum += (int)substr($num, 3, 1)*5;
$controlSum += (int)substr($num, 4, 1)*8;
$controlSum += (int)substr($num, 5, 1)*4;
$controlSum += (int)substr($num, 6, 1)*2;
$controlSum = $controlSum%11;
if ($controlSum == 0) {
return ($controlSum == $control) ? true : false;
} elseif ($controlSum >= 2 && $controlSum <= 10 ) {
return ((11 - $controlSum) == $control) ? true : false;
}
}
Usually there is a checksum for all of these types of IDs, so it's not just a matter of running a regex on it, but I can't find anything relating to Estonian businesses.
Any links to known libraries, examples would be appreciated. I'm working in PHP, but these could be in any language.
Thanks.
You can include a SOAP-Call from the European Commission https://ec.europa.eu/taxation_customs/vies/vatRequest.html
It should work like this (not 100% tested):
$vatId = "{{the vat id }}";
$client = new SoapClient("http://ec.europa.eu/taxation_customs/vies/checkVatService.wsdl");
if ($client) {
$cc = substr($vatId, 0, 2);
$vn = substr($vatId, 2);
$params = array('countryCode' => $cc, 'vatNumber' => $vn);
$result = $client->checkVat($params);
if ($result->valid == true) {
echo "VAT-ID ok";
}
} else {
die("connection failed");
}
I found this library, which answers my question
https://github.com/arthurdejong/python-stdnum/blob/master/stdnum/ee/registrikood.py
from stdnum.exceptions import *
from stdnum.util import clean, isdigits
def calc_check_digit(number):
"""Calculate the check digit."""
check = sum(((i % 9) + 1) * int(n)
for i, n in enumerate(number[:-1])) % 11
if check == 10:
check = sum((((i + 2) % 9) + 1) * int(n)
for i, n in enumerate(number[:-1])) % 11
return str(check % 10)
def compact(number):
"""Convert the number to the minimal representation. This strips the
number of any valid separators and removes surrounding whitespace."""
return clean(number, ' ').strip()
def validate(number):
"""Check if the number provided is valid. This checks the length and
check digit."""
number = compact(number)
if not isdigits(number):
raise InvalidFormat()
if len(number) != 8:
raise InvalidLength()
if number[0] not in '1789':
raise InvalidComponent()
if number[-1] != calc_check_digit(number):
raise InvalidChecksum()
return number
def is_valid(number):
"""Check if the number provided is valid. This checks the length and
check digit."""
try:
return bool(validate(number))
except ValidationError:
return False

How to calculate/display number as "Top 5", "Top 10", "Top 15" and so on?

How would you create a function (in PHP) to return a value for the appropriate rounded ranking group (e.g. "Top 5", "Top 10", "Top 15", ... "Top 1 million", "Top 5 million", "Top 10 million", "Top 15 million").
Note that it should round up to the nearest 1, 5 or 10 - e.g. "Top 15 million" rather than "Top 12 million".
Example:
Rank = Output
1-5 = 5 (Top 5 ranking)
6-10 = 10 (Top 10 ranking)
11-15 = 15 (Top 15 ranking)
This should work all the way up to trillions.
My current function works (sort of) however it would return a value for a ranking group of top 15 as top 20 instead.
private function format_number_iollions($amount,$style=null) {
$amount = (0 + str_replace(',', '', $amount));
if (!is_numeric($amount)){
return false;
}
$plusString = '';
switch ($style){
case 'plus':
$plusString = '+';
break;
}
if ($style==='rank' && $amount<=10){
return 10;
}
// filter and format it
if ($amount>1000000000000){
if ($style==='rank'){
$v = ceil(($amount/1000000000000));
} else {
$v = floor(($amount/1000000000000));
}
$v .= $plusString.' trillion';
} else if ($amount>1000000000){
if ($style==='rank'){
$v = ceil(($amount/1000000000));
} else {
$v = floor(($amount/1000000000));
}
$v .= $plusString.' billion';
} else if ($amount>1000000){
if ($style==='rank'){
$v = ceil(($amount/1000000));
} else {
$v = floor(($amount/1000000));
}
$v .= $plusString.' million';
} else if ($amount>100000){
if ($style==='rank'){
$v = ceil(($amount/100000));
} else {
$v = floor(($amount/100000));
}
$v .= '00,000'.$plusString;
} else if ($amount>10000){
if ($style==='rank'){
$v = ceil(($amount/10000));
} else {
$v = floor(($amount/10000));
}
$v .= '0,000'.$plusString;
} else if ($amount>1000){
if ($style==='rank'){
$v = ceil(($amount/1000));
} else {
$v = floor(($amount/1000));
}
$v .= ',000'.$plusString;
} else if ($amount>100){
if ($style==='rank'){
$v = ceil(($amount/100));
} else {
$v = floor(($amount/100));
}
$v .= '00'.$plusString;
} else if ($amount>10){
if ($style==='rank'){
$v = ceil(($amount/10));
} else {
$v = floor(($amount/10));
}
$v .= '0'.$plusString;
} else {
return number_format($amount);
}
return $v;
}
UPDATE - the final solution is this function (if anyone needs it):
private function get_rank_group($rawrank) {
// Divide by 1000 and count how many divisions were done
$rank_scale = 0;
while ($rawrank >= 1000) {
$rawrank /= 1000;
$rank_scale++;
}
// Determine which Top X can be
if ($rawrank >= 100) {
$lim_name = (floor(($rawrank-1) / 50) + 1) * 50;
} else {
$lim_name = (floor(($rawrank-1) / 5) + 1) * 5;
}
// if its in the next higher level
if ($lim_name >= 1000) {
$lim_name = '1';
$rank_scale++;
}
static $rank_scale_names = array('', ',000', ' Million', ' Billion', ' Trillion', ' Quadrillion', ' Quintillion', ' Sextillion', ' Septillion');
if (!isset($rank_scale_names[$rank_scale])){
return null; //too much - add higher word-numbers to $rank_scale_names
} else {
return "$lim_name{$rank_scale_names[$rank_scale]}";
}
}
<?php
function get_rank_group($rawrank) {
// Divide by 1000 and count how many divisions were done
$rank_scale = 0;
while ($rawrank >= 1000) {
$rawrank /= 1000;
$rank_scale++;
}
// Determine which Top X can be
static $rank_split = array(3, 5, 10, 15, 20, 30, 50, 100, 150, 200, 300, 500);
$lim_name = false;
// Look for a group, which is not less than the number
foreach ($rank_split as $lim) {
if ($rawrank <= $lim) {
$lim_name = $lim;
break;
}
}
// If nothing was found then it is a Top 1 of (next scaler) eg. 501 is Top 1 Thousand
if ($lim_name === false) {
$lim_name = '1';
$rank_scale++;
}
static $rank_scale_names = array('', ' Thousand', ' Million', ' Billion', ' Trillion', ' Quadrillion', ' Quintillion', ' Sextillion', ' Septillion'); // etc
if (!isset($rank_scale_names[$rank_scale])) return 'too much!'; // just check
return "Top $lim_name{$rank_scale_names[$rank_scale]}";
}
// Tests
$tests = array(1, 2, 3, 4, 16, 49, 50, 51, 299, 300, 301, 12345, 654321, 234567890, 1234567890123456789);
foreach ($tests as $test) {
print ("$test -> " . get_rank_group($test) . PHP_EOL);
}
The output:
1 -> Top 3
2 -> Top 3
3 -> Top 3
4 -> Top 5
16 -> Top 20
49 -> Top 50
50 -> Top 50
51 -> Top 100
299 -> Top 300
300 -> Top 300
301 -> Top 500
12345 -> Top 15 Thousand
654321 -> Top 1 Million
234567890 -> Top 300 Million
1234567890123456789 -> Top 3 Quintillion
As an improvement of AterLux's Solution
...
// Determine which Top X can be
if ($rawrank >= 100) {
$lim_name = (floor(($rawrank-1) / 50) + 1) * 50;
} else {
$lim_name = (floor(($rawrank-1) / 5) + 1) * 5;
}
// if its in the next higher level
if ($lim_name >= 1000) {
$lim_name = '1';
$rank_scale++;
}
static $rank_scale_names = array( ...
Output
1 -> Top 5
5 -> Top 5
9 -> Top 10
10 -> Top 10
12 -> Top 15
16 -> Top 20
49 -> Top 50
50 -> Top 50
51 -> Top 55
299 -> Top 300
300 -> Top 300
301 -> Top 350
995 -> Top 1 Thousand
12345 -> Top 15 Thousand
654321 -> Top 700 Thousand
234567890 -> Top 250 Million
1234567890123456789 -> Top 5 Quintillion

Return last values from specific position in array

There's an array of pages,
$pages = array(
1,
2,
3,
...
100,
101
);
And there's a variable $current_page. All I'm trying to do is pagination in digg-like style, so that it would look like this,
< 4 5 6 7 .... 15 16 17 18 >
The first thing that comes to mind is to get last and previous array values from specific position that equals to $current_page.
So I started with a basic for loop, but the problem is that amount of pages could be very large, so I don't think that's an efficient thing to do. Is there any another proper way of doing this? (maybe via native array_* functions)?
The following function builds StackOverflow like pagination. The objectives are:
First and last links must be visible always
Previous and next links must be visible always
At most 4 links before/after the current page should be visible
While the following function displays the complete pager, we are primarily interested in how to calculate the surrounding pages a and b as a function of current page, pager size and page count.
function so_like_pager($current_page, $page_count, $pager_size = 4) {
if ($current_page <= $pager_size) {
// the pager for first 4 pages starts from 1
$a = 1;
$b = min(1 + $pager_size, $page_count);
} else {
// the pager for remaining pages ends at current page + 2
// and starts so that 4 links are shown
$b = min($current_page + ($pager_size >> 1), $page_count);
$a = $b - $pager_size;
}
// return array("show_from" => $a, "show_upto" => $b);
echo '<p>';
if ($current_page !== 1) {
echo '' . 1 . ' ';
} else {
echo '<b>' . 1 . '</b> ';
}
if ($a > 1 + 1) {
echo '<span>...</span> ';
}
for ($i = $a; $i <= $b; $i++) {
if ($i !== 1 && $i !== $page_count) {
if ($current_page !== $i) {
echo '' . $i . ' ';
} else {
echo '<b>' . $i . '</b> ';
}
}
}
if ($b < $page_count - 1) {
echo '<span>...</span> ';
}
if ($current_page !== $page_count) {
echo '' . $page_count . ' ';
} else {
echo '<b>' . $page_count . '</b> ';
}
echo '</p>';
}
function so_like_pager_page($page) {
return 'page-' . $page . '/';
}
Tests:
so_like_pager(1, 100);
so_like_pager(2, 100);
so_like_pager(3, 100);
so_like_pager(4, 100);
so_like_pager(5, 100);
so_like_pager(6, 100);
so_like_pager(50, 100);
so_like_pager(99, 100);
so_like_pager(100, 100);
Output:
PS: Note: I ported this function from ASP classic to PHP in a hurry and did not test against edge cases.
function get_surrounding_pages(array $pages, $current, $amount = 2) {
$pages_idx = array_flip($pages);
if (!isset($pages_idx[$current])) {
return false;
}
$offset = max(0, $pages_idx[$current] - $amount);
$limit = $amount + 1 + ($pages_idx[$current] - $offset);
return array_slice($pages, $offset, $limit);
}
$pages = range(1, 1000);
$current = 42;
get_surrounding_pages($pages, $current, 4);
// array(38, 39, 40, 41, 42, 43, 44, 45, 46)
// this will work even if your number of pages is not continuous (eg: 1, 2, 6).
$pages = array(1, 2, 5, 6, 42, 234, 1048);
$current = 6;
get_surrounding_pages($pages, $current, 2);
// array(2, 5, 6, 42, 234)
// also works if you reach the boundaries
$pages = range(1, 10);
$current = 2;
get_surrounding_pages($pages, $current, 2);
// array(1, 2, 3, 4)
$current = 9;
get_surrounding_pages($pages, $current, 2);
// array(7, 8, 9, 10)
// returns false if you ask a page that doesn't exists
$pages = range(1, 10);
$current = 42;
get_surrounding_pages($pages, $current, 2);
// false
You can use PHP's end function to get the last entry of an array.
<?php
$a = array( 1, 2, 3, 4, 5, 10 );
echo end($a);
You can also use array_slice, or array_splice to cut an array, or array_pop, which removes the last element and returns what was removed.
Maybe you can try end. Should be more efficient than looping.

PHP foreach Check which post so far

Using for each I am dismantling a series of dates.
foreach( $ds as $d){
echo '<div class="bkback" onclick="bkdates(this);">'.date('M', strtotime("today + $d day")).'<br /><span class="bknum">'.date('d', strtotime("today + $d day")).'</span><br />
'.date('D', strtotime("today + $d day")).'</div>';
}
What I want to do is every put a marker <div class="marker"></div> after post 5, 10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, and 85 for the purposes of a jumping Jquery scroller.
So I need away of identifying which post we are at so far.
Any ideas?
Marvellous
Increment a variable each time through the loop - something like:
$i = 0;
foreach( $ds as $d) {
if ($i++ % 5 == 0) {
echo '<div class="marker"></div>';
}
}
I would just increment a counter and then check to see if it's divisible by 5.
$acounter = 0;
foreach( $ds as $d){
$acounter++;
if ( $acounter % 5 == 0 ) echo '<div class="marker"></div>';
echo '<div class="bkback" onclick="bkdates(this);">'.date('M', strtotime("today + $d day")).'<br /><span class="bknum">'.date('d', strtotime("today + $d day")).'</span><br />'.date('D', strtotime("today + $d day")).'</div>';
}
$counter = 0;
foreach( $ds as $d){
echo '<div class="bkback" onclick="bkdates(this);">'.date('M', strtotime("today + $d day")).'<br /><span class="bknum">'.date('d', strtotime("today + $d day")).'</span><br />
'.date('D', strtotime("today + $d day")).'</div>';
$counter++;
if ($counter % 5 == 0) { echo '<div class="marker"></div>'; }
}

generate smooth hex gradient table

generate list of hex colours?
Hi there,
Currently I am trying to generate a list of 50 hex colours that create a roughly smooth gradient from white to black, with all the colours in between.
How would I go about doing this in php?
Well,
Although The colours could prob be in a better order,
Here was my working.
<?php
function Gradient($HexFrom, $HexTo, $ColorSteps) {
$FromRGB['r'] = hexdec(substr($HexFrom, 0, 2));
$FromRGB['g'] = hexdec(substr($HexFrom, 2, 2));
$FromRGB['b'] = hexdec(substr($HexFrom, 4, 2));
$ToRGB['r'] = hexdec(substr($HexTo, 0, 2));
$ToRGB['g'] = hexdec(substr($HexTo, 2, 2));
$ToRGB['b'] = hexdec(substr($HexTo, 4, 2));
$StepRGB['r'] = ($FromRGB['r'] - $ToRGB['r']) / ($ColorSteps - 1);
$StepRGB['g'] = ($FromRGB['g'] - $ToRGB['g']) / ($ColorSteps - 1);
$StepRGB['b'] = ($FromRGB['b'] - $ToRGB['b']) / ($ColorSteps - 1);
$GradientColors = array();
for($i = 0; $i <= $ColorSteps; $i++) {
$RGB['r'] = floor($FromRGB['r'] - ($StepRGB['r'] * $i));
$RGB['g'] = floor($FromRGB['g'] - ($StepRGB['g'] * $i));
$RGB['b'] = floor($FromRGB['b'] - ($StepRGB['b'] * $i));
$HexRGB['r'] = sprintf('%02x', ($RGB['r']));
$HexRGB['g'] = sprintf('%02x', ($RGB['g']));
$HexRGB['b'] = sprintf('%02x', ($RGB['b']));
$GradientColors[] = implode(NULL, $HexRGB);
}
$GradientColors = array_filter($GradientColors, "len");
return $GradientColors;
}
function len($val){
return (strlen($val) == 6 ? true : false );
}
$count = 0;
$steps = 9;
$Gradients = Gradient("FFFFFF", "FF0000", $steps);
foreach($Gradients as $Gradient)
echo '<div style="background-color: #' . strtoupper($Gradient) . '">' . htmlentities('<option value="' . strtoupper($Gradient) . '">' . strtoupper($Gradient) . '</option>') . '</div>';
$count += count($Gradients);
$Gradients = Gradient("df1f00", "00FF00", $steps);
foreach($Gradients as $Gradient)
echo '<div style="background-color: #' . strtoupper($Gradient) . '">' . htmlentities('<option value="' . strtoupper($Gradient) . '">' . strtoupper($Gradient) . '</option>') . '</div>';
$count += count($Gradients);
$Gradients = Gradient("00df1f", "0000FF", $steps);
foreach($Gradients as $Gradient)
echo '<div style="background-color: #' . strtoupper($Gradient) . '">' . htmlentities('<option value="' . strtoupper($Gradient) . '">' . strtoupper($Gradient) . '</option>') . '</div>';
$count += count($Gradients);
$Gradients = Gradient("0000df", "000000", $steps);
foreach($Gradients as $Gradient)
echo '<div style="background-color: #' . $Gradient . '">' . htmlentities('<option value="' . $Gradient . '">' . $Gradient . '</option>') . '</div>';
$count += count($Gradients);
echo 'count: ' . $count;
with all the colours in between
You can find a path from white to black but you will have a difficult time including all colours - colour space is 3-dimensional, not linear.
You could look at this for some ideas:
http://www.exorithm.com/algorithm/view/create_gradient
Here is some customisation of the function, it's also handle case only 1 or 2 graduation needed, and also remove # on hexa before calculs and send back an array of color starting with #. so that can be directly applied to a content.
public function gradient($from_color, $to_color, $graduations = 10) {
$graduations--;
$startcol = str_replace("#", "", $from_color);
$endcol = str_replace("#", "", $to_color);
$RedOrigin = hexdec(substr($startcol, 0, 2));
$GrnOrigin = hexdec(substr($startcol, 2, 2));
$BluOrigin = hexdec(substr($startcol, 4, 2));
if ($graduations >= 2) { // for at least 3 colors
$GradientSizeRed = (hexdec(substr($endcol, 0, 2)) - $RedOrigin) / $graduations; //Graduation Size Red
$GradientSizeGrn = (hexdec(substr($endcol, 2, 2)) - $GrnOrigin) / $graduations;
$GradientSizeBlu = (hexdec(substr($endcol, 4, 2)) - $BluOrigin) / $graduations;
for ($i = 0; $i <= $graduations; $i++) {
$RetVal[$i] = strtoupper("#" . str_pad(dechex($RedOrigin + ($GradientSizeRed * $i)), 2, '0', STR_PAD_LEFT) .
str_pad(dechex($GrnOrigin + ($GradientSizeGrn * $i)), 2, '0', STR_PAD_LEFT) .
str_pad(dechex($BluOrigin + ($GradientSizeBlu * $i)), 2, '0', STR_PAD_LEFT));
}
} elseif ($graduations == 1) { // exactlly 2 colors
$RetVal[] = $from_color;
$RetVal[] = $to_color;
} else { // one color
$RetVal[] = $from_color;
}
return $RetVal;
}
Also could be helpful to take a look at my answer here.
It has the code to convert any number from any given range (let's say [0,20]) to particular color where 0 is red, 10 is yellow, 20 is green. You can use any colors and even use combination of 4 colors, so it's red - yellow - green - blue.
function rgb2hex($rgb) {
$hex = "#";
$hex .= str_pad(dechex($rgb[0]), 2, "0", STR_PAD_LEFT);
$hex .= str_pad(dechex($rgb[1]), 2, "0", STR_PAD_LEFT);
$hex .= str_pad(dechex($rgb[2]), 2, "0", STR_PAD_LEFT);
return $hex; // returns the hex value including the number sign (#)
}
function dif($c1, $c2, $percent) {
$array=array();
for($x=0;$x<3;$x++) {
$d=($c2[$x]-$c1[$x])*$percent+$c1[$x];
$array[]=(int)$d;
}
return $array;
}
for($x=0;$x<=255;$x++) {
echo rgb2hex( dif(array(255,255,255), array(0,0,0), $x/255 /*percent*/) );
echo "<br>";
}

Categories