Related
Does anyone know how to convert a Hex color to HSL in PHP? I've searched but the functions that I’ve found don’t convert precisely the color.
I think the mistake in the second answer is using integer division rather than fmod() when calculating the hue when red is the maximum colour value $hue = (($green - $blue) / $delta) % 6;
I think the mistake in the first answer is in the saturation calculation -
for me $s = $l > 0.5 ? $diff / (2 - $max - $min) : $diff / ($max + $min); is a bit confusing to unpick
Since usually when I want to convert RGB to HSL I want to adjust the lightness value to make a lighter or darker version of the same colour, I have built that into the function below by adding an optional $ladj percent value.
The $hex parameter can be either a hex string (with or without the '#') or an array of RGB values (between 0 and 255)
The return value is an HSL string ready to drop straight in to a CSS colour. ie the values are from 0 to 359 for hue and 0 to 100% for saturation and lightness.
I think this works correctly (based on https://gist.github.com/brandonheyer/5254516)
function hex2hsl($RGB, $ladj = 0) {
//have we got an RGB array or a string of hex RGB values (assume it is valid!)
if (!is_array($RGB)) {
$hexstr = ltrim($RGB, '#');
if (strlen($hexstr) == 3) {
$hexstr = $hexstr[0] . $hexstr[0] . $hexstr[1] . $hexstr[1] . $hexstr[2] . $hexstr[2];
}
$R = hexdec($hexstr[0] . $hexstr[1]);
$G = hexdec($hexstr[2] . $hexstr[3]);
$B = hexdec($hexstr[4] . $hexstr[5]);
$RGB = array($R,$G,$B);
}
// scale the RGB values to 0 to 1 (percentages)
$r = $RGB[0]/255;
$g = $RGB[1]/255;
$b = $RGB[2]/255;
$max = max( $r, $g, $b );
$min = min( $r, $g, $b );
// lightness calculation. 0 to 1 value, scale to 0 to 100% at end
$l = ( $max + $min ) / 2;
// saturation calculation. Also 0 to 1, scale to percent at end.
$d = $max - $min;
if( $d == 0 ){
// achromatic (grey) so hue and saturation both zero
$h = $s = 0;
} else {
$s = $d / ( 1 - abs( (2 * $l) - 1 ) );
// hue (if not grey) This is being calculated directly in degrees (0 to 360)
switch( $max ){
case $r:
$h = 60 * fmod( ( ( $g - $b ) / $d ), 6 );
if ($b > $g) { //will have given a negative value for $h
$h += 360;
}
break;
case $g:
$h = 60 * ( ( $b - $r ) / $d + 2 );
break;
case $b:
$h = 60 * ( ( $r - $g ) / $d + 4 );
break;
} //end switch
} //end else
// make any lightness adjustment required
if ($ladj > 0) {
$l += (1 - $l) * $ladj/100;
} elseif ($ladj < 0) {
$l += $l * $ladj/100;
}
//put the values in an array and scale the saturation and lightness to be percentages
$hsl = array( round( $h), round( $s*100), round( $l*100) );
//we could return that, but lets build a CSS compatible string and return that instead
$hslstr = 'hsl('.$hsl[0].','.$hsl[1].'%,'.$hsl[2].'%)';
return $hslstr;
}
In real life I would break out the hex string to RGB array conversion and the percentage adjustment into separate functions, but have included them here for completeness.
You could also use the percent adjustment to shift the hue or saturation once you've got the colour in HSL format.
function hexToHsl($hex) {
$hex = array($hex[0].$hex[1], $hex[2].$hex[3], $hex[4].$hex[5]);
$rgb = array_map(function($part) {
return hexdec($part) / 255;
}, $hex);
$max = max($rgb);
$min = min($rgb);
$l = ($max + $min) / 2;
if ($max == $min) {
$h = $s = 0;
} else {
$diff = $max - $min;
$s = $l > 0.5 ? $diff / (2 - $max - $min) : $diff / ($max + $min);
switch($max) {
case $rgb[0]:
$h = ($rgb[1] - $rgb[2]) / $diff + ($rgb[1] < $rgb[2] ? 6 : 0);
break;
case $rgb[1]:
$h = ($rgb[2] - $rgb[0]) / $diff + 2;
break;
case $rgb[2]:
$h = ($rgb[0] - $rgb[1]) / $diff + 4;
break;
}
$h /= 6;
}
return array($h, $s, $l);
}
Rewritten (and adjusted a bit) from javascript from https://css-tricks.com/converting-color-spaces-in-javascript/
<?php
function hexToHsl($hex)
{
$red = hexdec(substr($hex, 0, 2)) / 255;
$green = hexdec(substr($hex, 2, 2)) / 255;
$blue = hexdec(substr($hex, 4, 2)) / 255;
$cmin = min($red, $green, $blue);
$cmax = max($red, $green, $blue);
$delta = $cmax - $cmin;
if ($delta === 0) {
$hue = 0;
} elseif ($cmax === $red) {
$hue = (($green - $blue) / $delta) % 6;
} elseif ($cmax === $green) {
$hue = ($blue - $red) / $delta + 2;
} else {
$hue = ($red - $green) / $delta + 4;
}
$hue = round($hue * 60);
if ($hue < 0) {
$hue += 360;
}
$lightness = (($cmax + $cmin) / 2) * 100;
$saturation = $delta === 0 ? 0 : ($delta / (1 - abs(2 * $lightness - 1))) * 100;
if ($saturation < 0) {
$saturation += 100;
}
$lightness = round($lightness);
$saturation = round($saturation);
return "hsl(${hue}, ${saturation}%, ${lightness}%)";
}
Example:
<?php
echo hexToHsl('fbffe0'); // outputs 'hsl(68, 100%, 94%)'
I have this code which returns:
float(-70.869444444444)
I can see using var_dump that it finds all coordinates, but only one is displayed.
How do I modify below code to display all coordinates transferred to degree format and also display text?
Wanted result should be:
-70.869 -6.35 test -127.49 more test second line -127.35
Code
$str60 = 'W07052 W0623 test E12727 more test second line E12725';
preg_match_all('/([EWSN])([0-9]{3})([0-9]{2})/ms', $str60, $matches);
$result60 = DMS2Decimal($degrees = (int) $matches[2][0], $minutes = (int) $matches[3][0], $seconds = 10, $direction = strtolower($matches[1][0]));
function DMS2Decimal($degrees = 0, $minutes = 0, $seconds = 0, $direction = 'n')
{
//converts DMS coordinates to decimal
//returns false on bad inputs, decimal on success
//direction must be n, s, e or w, case-insensitive
$d = strtolower($direction);
$ok = array('n', 's', 'e', 'w');
//degrees must be integer between 0 and 180
if (!is_numeric($degrees) || $degrees < 0 || $degrees > 180) {
$decimal = false;
//var_dump($decimal);
}
//minutes must be integer or float between 0 and 59
elseif (!is_numeric($minutes) || $minutes < 0 || $minutes > 59) {
$decimal = false;
//var_dump($decimal);
}
//seconds must be integer or float between 0 and 59
elseif (!is_numeric($seconds) || $seconds < 0 || $seconds > 59) {
$decimal = false;
} elseif (!in_array($d, $ok)) {
$decimal = false;
//var_dump($decimal);
} else {
//inputs clean, calculate
$decimal = $degrees + ($minutes / 60) + ($seconds / 3600);
//reverse for south or west coordinates; north is assumed
if ($d == 's' || $d == 'w') {
$decimal *= -1;
}
}
return $decimal;
}
var_dump($matches);
var_dump($result60);
Yes, preg_replace_callback is what you want. I tweaked your regular expression as I assumed you wanted to catch"W0623."
I also cleaned up your function a bit, though the only change in output was to round it to 3 decimal places. The rest was just organizing your conditions and can be ignored if you prefer.
function DMS2Decimal($degrees = 0, $minutes = 0, $seconds = 0, $direction = 'n')
{
//converts DMS coordinates to decimal
//returns false on bad inputs, decimal on success
$d = strtolower($direction);
if (
//degrees must be integer between 0 and 180
(is_numeric($degrees) && $degrees >= 0 && $degrees <= 180) &&
//minutes must be integer or float between 0 and 59
(is_numeric($minutes) && $minutes >= 0 && $minutes <= 59) &&
//seconds must be integer or float between 0 and 59
(is_numeric($seconds) && $seconds >= 0 && $seconds <= 59) &&
//direction must be n, s, e or w, case-insensitive
(in_array($d, ['n', 's', 'e', 'w']))
) {
$decimal = $degrees + ($minutes / 60) + ($seconds / 3600);
//reverse for south or west coordinates; north is assumed
if ($d == 's' || $d == 'w') {
$decimal *= -1;
}
return round($decimal, 3);
} else {
return false;
}
}
$str60 = 'W07052 W0623 test E12727 more test second line E12725';
$result60 = preg_replace_callback(
"/([EWSN])([0-9]{3})([0-9]{1,2})/ms",
function($m) {return DMS2Decimal((int)$m[2], (int)$m[3], 10, $m[1]);},
$str60
);
echo $result60;
Output:
-70.869 -62.053 test 127.453 more test second line 127.419
The script below might return your desired result. I'm not sure if I have given correct input values to the function, it is based on this, where NWSE are the direction, the next {2}digits might be Degree, {2} digits after that might be the Minute and the remaining digits might be the Second, which may or may not be a float number.
$str60 = 'N404536 W73592.4 test E73592.4 more test second line S73592.4';
$split_strings = preg_split('/\s/s', $str60);
foreach ($split_strings as $value) {
if (preg_match('/([EWSN][0-9\.]{4,})/ms', $value)) {
$result60 .= DMS2Decimal(
$degrees = (int) substr($value, 1, 2), // Not sure, this might return degrees - e.g., W{73}592.4
$minutes = (int) substr($value, 3, 2), // Not sure, this might return minutes - e.g., W735{92}.4
$seconds = substr($value, 5) != null ? (int) substr($value, 5) : 0, // Not sure, this checks if second is available or place zero - e.g., W7359{2.4}
$direction = substr($value, 0, 1) // This is direction East West North South - e.g., {W}73592.4
) . " ";
} else {
$result60 .= $value . " ";
}
}
var_dump($result60);
function DMS2Decimal($degrees = 0, $minutes = 0, $seconds = 0, $direction = 'n')
{
//converts DMS coordinates to decimal
//returns false on bad inputs, decimal on success
//direction must be n, s, e or w, case-insensitive
$d = strtolower($direction);
$ok = array('n', 's', 'e', 'w');
//degrees must be integer between 0 and 180
if (!is_numeric($degrees) || $degrees < 0 || $degrees > 180) {
$decimal = false;
//var_dump($decimal);
}
//minutes must be integer or float between 0 and 59
elseif (!is_numeric($minutes) || $minutes < 0 || $minutes > 59) {
$decimal = false;
//var_dump($decimal);
}
//seconds must be integer or float between 0 and 59
elseif (!is_numeric($seconds) || $seconds < 0 || $seconds > 59) {
$decimal = false;
} elseif (!in_array($d, $ok)) {
$decimal = false;
//var_dump($decimal);
} else {
//inputs clean, calculate
$decimal = $degrees + ($minutes / 60) + ($seconds / 3600);
//reverse for south or west coordinates; north is assumed
if ($d == 's' || $d == 'w') {
$decimal *= -1;
}
}
return $decimal;
}
Output
string(56) "40.76 -73.984 test 73.984 more test second line -73.984 "
I am writing randomColorSelect() function, so that when the script is executed, prints the word RED 10% of the time, the word BLUE 50% of the time and the word GREEN 80% of the time: but not getting how to get this as i am new in PHP.
http://php.net/manual/en/function.rand.php
This should give the desired effect.
$red_dice = rand(0, 100); // 0% to 100%
$blue_dice = rand(0, 100); // 0% to 100%
$green_dice = rand(0, 100); // 0% to 100%
if ($red_dice <= 10)
{
echo "RED\n";
}
if ($blue_dice <= 50)
{
echo "BLUE\n";
}
if ($green_dice <= 80)
{
echo "GREEN\n";
}
Building on the believe that the percentages should add up to 100% total:
$number = rand(1, 100);
// 10%
if($number <= 10)
{
echo 'RED';
}
// 50%
else if(($number >= 11) && ($number <= 60))
{
echo 'BLUE';
}
// rest-%, in this case 30
else
{
echo 'GREEN';
}
This question already has answers here:
Format bytes to kilobytes, megabytes, gigabytes
(28 answers)
Closed 3 years ago.
How can I convert the output of PHP's filesize() function to a nice format with MegaBytes, KiloBytes etc?
like:
if the size is less than 1 MB, show the size in KB
if it's between 1 MB - 1 GB show it in MB
if it's larger - in GB
Here is a sample:
<?php
// Snippet from PHP Share: http://www.phpshare.org
function formatSizeUnits($bytes)
{
if ($bytes >= 1073741824)
{
$bytes = number_format($bytes / 1073741824, 2) . ' GB';
}
elseif ($bytes >= 1048576)
{
$bytes = number_format($bytes / 1048576, 2) . ' MB';
}
elseif ($bytes >= 1024)
{
$bytes = number_format($bytes / 1024, 2) . ' KB';
}
elseif ($bytes > 1)
{
$bytes = $bytes . ' bytes';
}
elseif ($bytes == 1)
{
$bytes = $bytes . ' byte';
}
else
{
$bytes = '0 bytes';
}
return $bytes;
}
?>
Even nicer is this version I created from a plugin I found:
function filesize_formatted($path)
{
$size = filesize($path);
$units = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB');
$power = $size > 0 ? floor(log($size, 1024)) : 0;
return number_format($size / pow(1024, $power), 2, '.', ',') . ' ' . $units[$power];
}
Note from filesize() doc
Because PHP's integer type is signed and many platforms use 32bit
integers, some filesystem functions may return unexpected results for
files which are larger than 2GB
A cleaner approach:
function Size($path)
{
$bytes = sprintf('%u', filesize($path));
if ($bytes > 0)
{
$unit = intval(log($bytes, 1024));
$units = array('B', 'KB', 'MB', 'GB');
if (array_key_exists($unit, $units) === true)
{
return sprintf('%d %s', $bytes / pow(1024, $unit), $units[$unit]);
}
}
return $bytes;
}
I think this is a better approach. Simple and straight forward.
public function sizeFilter( $bytes )
{
$label = array( 'B', 'KB', 'MB', 'GB', 'TB', 'PB' );
for( $i = 0; $bytes >= 1024 && $i < ( count( $label ) -1 ); $bytes /= 1024, $i++ );
return( round( $bytes, 2 ) . " " . $label[$i] );
}
This is based on #adnan's great answer.
Changes:
added internal filesize() call
return early style
saving one concatentation on 1 byte
And you can still pull the filesize() call out of the function, in order to get a pure bytes formatting function. But this works on a file.
/**
* Formats filesize in human readable way.
*
* #param file $file
* #return string Formatted Filesize, e.g. "113.24 MB".
*/
function filesize_formatted($file)
{
$bytes = filesize($file);
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' KB';
} elseif ($bytes > 1) {
return $bytes . ' bytes';
} elseif ($bytes == 1) {
return '1 byte';
} else {
return '0 bytes';
}
}
All the answers to the question uses that 1 kilobyte = 1024 bytes which is wrong! (1 kibibyte = 1024 bytes)
since the question asks to convert file sizes, it should use that 1 kilobyte = 1000 bytes (see https://wiki.ubuntu.com/UnitsPolicy)
function format_bytes($bytes, $precision = 2) {
$units = array('B', 'KB', 'MB', 'GB');
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1000));
$pow = min($pow, count($units) - 1);
$bytes /= pow(1000, $pow);
return round($bytes, $precision) . ' ' . $units[$pow];
}
This would be a cleaner implementation:
function size2Byte($size) {
$units = array('KB', 'MB', 'GB', 'TB');
$currUnit = '';
while (count($units) > 0 && $size > 1024) {
$currUnit = array_shift($units);
$size /= 1024;
}
return ($size | 0) . $currUnit;
}
A complete example.
<?php
$units = explode(' ','B KB MB GB TB PB');
echo("<html><body>");
echo('file size: ' . format_size(filesize("example.txt")));
echo("</body></html>");
function format_size($size) {
$mod = 1024;
for ($i = 0; $size > $mod; $i++) {
$size /= $mod;
}
$endIndex = strpos($size, ".")+3;
return substr( $size, 0, $endIndex).' '.$units[$i];
}
?>
function calcSize($size,$accuracy=2) {
$units = array('b','Kb','Mb','Gb');
foreach($units as $n=>$u) {
$div = pow(1024,$n);
if($size > $div) $output = number_format($size/$div,$accuracy).$u;
}
return $output;
}
function getNiceFileSize($file, $digits = 2){
if (is_file($file)) {
$filePath = $file;
if (!realpath($filePath)) {
$filePath = $_SERVER["DOCUMENT_ROOT"] . $filePath;
}
$fileSize = filesize($filePath);
$sizes = array("TB", "GB", "MB", "KB", "B");
$total = count($sizes);
while ($total-- && $fileSize > 1024) {
$fileSize /= 1024;
}
return round($fileSize, $digits) . " " . $sizes[$total];
}
return false;
}
//Get the size in bytes
function calculateFileSize($size)
{
$sizes = ['B', 'KB', 'MB', 'GB'];
$count=0;
if ($size < 1024) {
return $size . " " . $sizes[$count];
} else{
while ($size>1024){
$size=round($size/1024,2);
$count++;
}
return $size . " " . $sizes[$count];
}
}
Im trying to format the output of numbers in php. I have an amount of posts that show up, and next to each user is the total of posts. But it shows that actual amount, i want it to show it in a shorter format, actually, just like they do here at SO with reputation
any ideas?
<?
$numbers = array(100,1000,15141,3421);
function format_number($number) {
if($number >= 1000) {
return $number/1000 . "k"; // NB: you will want to round this
}
else {
return $number;
}
}
foreach($numbers as $number) {
echo $number . " : " . format_number($number);
echo "\n";
}
function count_format($n, $point='.', $sep=',') {
if ($n < 0) {
return 0;
}
if ($n < 10000) {
return number_format($n, 0, $point, $sep);
}
$d = $n < 1000000 ? 1000 : 1000000;
$f = round($n / $d, 1);
return number_format($f, $f - intval($f) ? 1 : 0, $point, $sep) . ($d == 1000 ? 'k' : 'M');
}
Use This
Shorten long numbers to K/M/B?
function number_format_short( $n, $precision = 1 ) {
if ($n < 900) {
// 0 - 900
$n_format = number_format($n, $precision);
$suffix = '';
} else if ($n < 900000) {
// 0.9k-850k
$n_format = number_format($n / 1000, $precision);
$suffix = 'K';
} else if ($n < 900000000) {
// 0.9m-850m
$n_format = number_format($n / 1000000, $precision);
$suffix = 'M';
} else if ($n < 900000000000) {
// 0.9b-850b
$n_format = number_format($n / 1000000000, $precision);
$suffix = 'B';
} else {
// 0.9t+
$n_format = number_format($n / 1000000000000, $precision);
$suffix = 'T';
}