├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── phpunit.dist.xml └── src └── Number.php /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | 3 | tests/ export-ignore 4 | .github/ export-ignore 5 | composer.lock export-ignore 6 | phpunit.xml export-ignore 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cache and logs (Symfony2) 2 | /app/cache/* 3 | /app/logs/* 4 | !app/cache/.gitkeep 5 | !app/logs/.gitkeep 6 | 7 | # Email spool folder 8 | /app/spool/* 9 | 10 | # Cache, session files and logs (Symfony3) 11 | /var/cache/* 12 | /var/logs/* 13 | /var/sessions/* 14 | !var/cache/.gitkeep 15 | !var/logs/.gitkeep 16 | !var/sessions/.gitkeep 17 | 18 | # Logs (Symfony4) 19 | /var/log/* 20 | !var/log/.gitkeep 21 | 22 | # Parameters 23 | /app/config/parameters.yml 24 | /app/config/parameters.ini 25 | 26 | # Managed by Composer 27 | /app/bootstrap.php.cache 28 | /var/bootstrap.php.cache 29 | /bin/* 30 | !bin/console 31 | !bin/symfony_requirements 32 | /vendor/ 33 | 34 | # Assets and user uploads 35 | /web/bundles/ 36 | /web/uploads/ 37 | 38 | # PHPUnit 39 | /app/phpunit.xml 40 | /phpunit.xml 41 | 42 | # Build data 43 | /build/ 44 | 45 | # Composer PHAR 46 | /composer.phar 47 | 48 | # Backup entities generated with doctrine:generate:entities command 49 | **/Entity/*~ 50 | 51 | # Embedded web-server pid file 52 | /.web-server-pid 53 | 54 | # Code style 55 | .php-cs-fixer.cache 56 | 57 | # PhpStorm 58 | /.idea/ 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 FriendsOfPHP 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PHP Number Utility 2 | 3 | This library provides a set of utility functions for working with numbers in PHP, including formatting as currency, percentages, ordinals, and more. 4 | 5 | ## Installation 6 | 7 | ```bash 8 | composer require friendsofphp/number 9 | ``` 10 | 11 | ## Usage 12 | 13 | ### Basic usage 14 | 15 | ```php 16 | use FriendsOfPhp\Number\Number; 17 | 18 | // Format a number 19 | echo Number::format(1234567.89); // 1,234,567.89 20 | 21 | // Spell out a number 22 | echo Number::spell(1234567.89); // one thousand two hundred thirty-four 23 | 24 | // Get the ordinal form of a number 25 | echo Number::ordinal(1234567.89); // 42nd 26 | 27 | // Format a number as a percentage 28 | echo Number::percentage(1234567.89); // 1% 29 | 30 | // Format a number as currency 31 | echo Number::currency(1234567.89, 'EUR'); // €1,234.56 32 | 33 | // Format file size 34 | echo Number::fileSize(1234567.89); // 1 KB 35 | 36 | // Get a human-readable representation of a number 37 | echo Number::format(1234567.89); // 1 million 38 | 39 | // Get the abbreviated form of a number 40 | echo Number::format(1234567.89); // 1M 41 | ``` 42 | 43 | ### Advanced usage 44 | 45 | ```php 46 | use FriendsOfPhp\Number\Number; 47 | 48 | // Set a custom locale 49 | Number::useLocale('fr'); 50 | 51 | // Use the custom locale for formatting 52 | $formattedNumber = Number::format(1234.56); 53 | 54 | // Change the precision when formatting 55 | $preciseNumber = Number::format(1234.56789, 2); 56 | 57 | // Use a custom locale for currency formatting 58 | $currencyFormatted = Number::currency(1234.56, 'GBP', 'fr'); 59 | ``` 60 | 61 | ## Information 62 | 63 | ### Documentation 64 | 65 | You can visit the documentation at [friendsofphp.github.io/number](https://friendsofphp.github.io/number/) 66 | 67 | ### License 68 | 69 | This package is open-sourced software licensed under the [MIT License](LICENSE). 70 | 71 | ### Attributions 72 | 73 | The Number utility is ported from Laravel, licensed under the MIT Licence. 74 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "friendsofphp/number", 3 | "description": "Number utility for displaying human-friendly numeric data", 4 | "license": "MIT", 5 | "autoload": { 6 | "psr-4": { 7 | "FriendsOfPhp\\Number\\": "src/" 8 | } 9 | }, 10 | "authors": [ 11 | { 12 | "name": "Caen De Silva", 13 | "email": "caen@desilva.se" 14 | } 15 | ], 16 | "require": { 17 | "php": "^8.1 <8.4", 18 | "ext-intl": "*" 19 | }, 20 | "require-dev": { 21 | "pestphp/pest": "^2.24", 22 | "friendsofphp/php-cs-fixer": "^3.40" 23 | }, 24 | "config": { 25 | "allow-plugins": { 26 | "pestphp/pest-plugin": true 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /phpunit.dist.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | ./tests 10 | 11 | 12 | 13 | 14 | ./app 15 | ./src 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/Number.php: -------------------------------------------------------------------------------- 1 | setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $maxPrecision); 31 | } elseif (!is_null($precision)) { 32 | $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $precision); 33 | } 34 | 35 | return $formatter->format($number); 36 | } 37 | 38 | /** 39 | * Spell out the given number in the given locale. 40 | * 41 | * @param int|float $number 42 | * @param ?string $locale 43 | * @return string 44 | */ 45 | public static function spell(int|float $number, ?string $locale = null): string 46 | { 47 | $formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::SPELLOUT); 48 | 49 | return $formatter->format($number); 50 | } 51 | 52 | /** 53 | * Convert the given number to ordinal form. 54 | * 55 | * @param int|float $number 56 | * @param ?string $locale 57 | * @return string 58 | */ 59 | public static function ordinal(int|float $number, ?string $locale = null): string 60 | { 61 | $formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::ORDINAL); 62 | 63 | return $formatter->format($number); 64 | } 65 | 66 | /** 67 | * Convert the given number to its percentage equivalent. 68 | * 69 | * @param int|float $number 70 | * @param int $precision 71 | * @param int|null $maxPrecision 72 | * @param ?string $locale 73 | * @return string|false 74 | */ 75 | public static function percentage(int|float $number, int $precision = 0, ?int $maxPrecision = null, ?string $locale = null): bool|string 76 | { 77 | $formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::PERCENT); 78 | 79 | if (!is_null($maxPrecision)) { 80 | $formatter->setAttribute(NumberFormatter::MAX_FRACTION_DIGITS, $maxPrecision); 81 | } else { 82 | $formatter->setAttribute(NumberFormatter::FRACTION_DIGITS, $precision); 83 | } 84 | 85 | return $formatter->format($number / 100); 86 | } 87 | 88 | /** 89 | * Convert the given number to its currency equivalent. 90 | * 91 | * @param int|float $number 92 | * @param string $in 93 | * @param ?string $locale 94 | * @return string|false 95 | */ 96 | public static function currency(int|float $number, string $in = 'USD', ?string $locale = null): bool|string 97 | { 98 | $formatter = new NumberFormatter($locale ?? static::$locale, NumberFormatter::CURRENCY); 99 | 100 | return $formatter->formatCurrency($number, $in); 101 | } 102 | 103 | /** 104 | * Convert the given number to its file size equivalent. 105 | * 106 | * @param int|float $bytes 107 | * @param int $precision 108 | * @param int|null $maxPrecision 109 | * @return string 110 | */ 111 | public static function fileSize(int|float $bytes, int $precision = 0, ?int $maxPrecision = null): string 112 | { 113 | $units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']; 114 | 115 | for ($i = 0; ($bytes / 1024) > 0.9 && ($i < count($units) - 1); $i++) { 116 | $bytes /= 1024; 117 | } 118 | 119 | return sprintf('%s %s', static::format($bytes, $precision, $maxPrecision), $units[$i]); 120 | } 121 | 122 | /** 123 | * Convert the number to its human-readable equivalent with abbreviated units. 124 | * 125 | * @param int|float $number 126 | * @param int $precision 127 | * @param int|null $maxPrecision 128 | * @return string 129 | */ 130 | public static function abbreviate(int|float $number, int $precision = 0, ?int $maxPrecision = null): string 131 | { 132 | return static::forHumans($number, $precision, $maxPrecision, abbreviate: true); 133 | } 134 | 135 | /** 136 | * Convert the number to its human-readable equivalent. 137 | * 138 | * @param int $number 139 | * @param int $precision 140 | * @param int|null $maxPrecision 141 | * @return string 142 | */ 143 | public static function forHumans(int|float $number, int $precision = 0, ?int $maxPrecision = null, bool $abbreviate = false): string 144 | { 145 | return static::summarize($number, $precision, $maxPrecision, $abbreviate ? [ 146 | 3 => 'K', 147 | 6 => 'M', 148 | 9 => 'B', 149 | 12 => 'T', 150 | 15 => 'Q', 151 | ] : [ 152 | 3 => ' thousand', 153 | 6 => ' million', 154 | 9 => ' billion', 155 | 12 => ' trillion', 156 | 15 => ' quadrillion', 157 | ]); 158 | } 159 | 160 | /** 161 | * Convert the number to its human-readable equivalent. 162 | * 163 | * @param int $number 164 | * @param int $precision 165 | * @param int|null $maxPrecision 166 | * @param array $units 167 | * @return string 168 | */ 169 | protected static function summarize(int|float $number, int $precision = 0, ?int $maxPrecision = null, array $units = []): string 170 | { 171 | if (empty($units)) { 172 | $units = [ 173 | 3 => 'K', 174 | 6 => 'M', 175 | 9 => 'B', 176 | 12 => 'T', 177 | 15 => 'Q', 178 | ]; 179 | } 180 | 181 | switch (true) { 182 | case $number === 0: 183 | return '0'; 184 | case $number < 0: 185 | return sprintf('-%s', static::summarize(abs($number), $precision, $maxPrecision, $units)); 186 | case $number >= 1e15: 187 | return sprintf('%s'.end($units), static::summarize($number / 1e15, $precision, $maxPrecision, $units)); 188 | } 189 | 190 | $numberExponent = floor(log10($number)); 191 | $displayExponent = $numberExponent - ($numberExponent % 3); 192 | $number /= pow(10, $displayExponent); 193 | 194 | return trim(sprintf('%s%s', static::format($number, $precision, $maxPrecision), $units[$displayExponent] ?? '')); 195 | } 196 | 197 | /** 198 | * Set the default locale. 199 | * 200 | * @param string $locale 201 | * @return void 202 | */ 203 | public static function useLocale(string $locale): void 204 | { 205 | static::$locale = $locale; 206 | } 207 | } 208 | --------------------------------------------------------------------------------