Skip to content

Instantly share code, notes, and snippets.

@bupy7
Created December 14, 2015 08:34
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save bupy7/26827cec44f4a8c01ff3 to your computer and use it in GitHub Desktop.
Save bupy7/26827cec44f4a8c01ff3 to your computer and use it in GitHub Desktop.
Сумма прописью
<?php
/**
* Возвращает сумму прописью
* @author runcore
* @uses morph(...)
*/
function num2str($num) {
$nul='ноль';
$ten=array(
array('','один','два','три','четыре','пять','шесть','семь', 'восемь','девять'),
array('','одна','две','три','четыре','пять','шесть','семь', 'восемь','девять'),
);
$a20=array('десять','одиннадцать','двенадцать','тринадцать','четырнадцать' ,'пятнадцать','шестнадцать','семнадцать','восемнадцать','девятнадцать');
$tens=array(2=>'двадцать','тридцать','сорок','пятьдесят','шестьдесят','семьдесят' ,'восемьдесят','девяносто');
$hundred=array('','сто','двести','триста','четыреста','пятьсот','шестьсот', 'семьсот','восемьсот','девятьсот');
$unit=array( // Units
array('копейка' ,'копейки' ,'копеек', 1),
array('рубль' ,'рубля' ,'рублей' ,0),
array('тысяча' ,'тысячи' ,'тысяч' ,1),
array('миллион' ,'миллиона','миллионов' ,0),
array('миллиард','милиарда','миллиардов',0),
);
//
list($rub,$kop) = explode('.',sprintf("%015.2f", floatval($num)));
$out = array();
if (intval($rub)>0) {
foreach(str_split($rub,3) as $uk=>$v) { // by 3 symbols
if (!intval($v)) continue;
$uk = sizeof($unit)-$uk-1; // unit key
$gender = $unit[$uk][3];
list($i1,$i2,$i3) = array_map('intval',str_split($v,1));
// mega-logic
$out[] = $hundred[$i1]; # 1xx-9xx
if ($i2>1) $out[]= $tens[$i2].' '.$ten[$gender][$i3]; # 20-99
else $out[]= $i2>0 ? $a20[$i3] : $ten[$gender][$i3]; # 10-19 | 1-9
// units without rub & kop
if ($uk>1) $out[]= morph($v,$unit[$uk][0],$unit[$uk][1],$unit[$uk][2]);
} //foreach
}
else $out[] = $nul;
$out[] = morph(intval($rub), $unit[1][0],$unit[1][1],$unit[1][2]); // rub
$out[] = $kop.' '.morph($kop,$unit[0][0],$unit[0][1],$unit[0][2]); // kop
return trim(preg_replace('/ {2,}/', ' ', join(' ',$out)));
}
/**
* Склоняем словоформу
* @ author runcore
*/
function morph($n, $f1, $f2, $f5) {
$n = abs(intval($n)) % 100;
if ($n>10 && $n<20) return $f5;
$n = $n % 10;
if ($n>1 && $n<5) return $f2;
if ($n==1) return $f1;
return $f5;
}
@uonick
Copy link

uonick commented Feb 5, 2020

<?php
declare(strict_types=1);

/**
 * @author: runcore
 */
trait NumericString
{
    /**
     * Возвращает сумму прописью
     */
    public function num2str(int $num): string
    {
        $nul = 'ноль';
        $ten = [
            [
                '', 'один', 'два', 'три', 'четыре', 'пять', 'шесть', 'семь',
                'восемь', 'девять',
            ],
            [
                '', 'одна', 'две', 'три', 'четыре', 'пять', 'шесть', 'семь',
                'восемь', 'девять',
            ],
        ];
        $a20 = [
            'десять', 'одиннадцать', 'двенадцать', 'тринадцать', 'четырнадцать',
            'пятнадцать', 'шестнадцать', 'семнадцать', 'восемнадцать',
            'девятнадцать',
        ];
        $tens = [
            2 => 'двадцать', 'тридцать', 'сорок', 'пятьдесят', 'шестьдесят',
            'семьдесят', 'восемьдесят', 'девяносто',
        ];
        $hundred = [
            '', 'сто', 'двести', 'триста', 'четыреста', 'пятьсот', 'шестьсот',
            'семьсот', 'восемьсот', 'девятьсот',
        ];
        $unit = [
              ['копейка', 'копейки', 'копеек', 1],
              ['рубль', 'рубля', 'рублей', 0],
              ['тысяча', 'тысячи', 'тысяч', 1],
              ['миллион', 'миллиона', 'миллионов', 0],
              ['миллиард', 'милиарда', 'миллиардов', 0],
        ];
        [$rub, $kop] = explode('.', sprintf('%015.2f', (float) $num));
        $out = [];
        if ((int) $rub > 0) {
            foreach (str_split($rub, 3) as $uk => $v) {
                if (!(int) $v) {
                    continue;
                }
                $uk = sizeof($unit) - $uk - 1;
                $gender = $unit[$uk][3];
                $array_map = [];

                foreach (str_split($v, 1) as $key => $var) {
                    $array_map[$key] = (int) str_split($v, 1)[$key];
                }
                [$i1, $i2, $i3] = $array_map;

                $out[] = $hundred[$i1];
                if ($i2 > 1) {
                    $out[] = $tens[$i2].' '.$ten[$gender][$i3];
                } else {
                    $out[] = $i2 > 0 ? $a20[$i3] : $ten[$gender][$i3];
                } # 10-19 | 1-9
                // units without rub & kop
                if ($uk > 1) {
                    $out[] = $this->morph(
                        $v,
                        $unit[$uk][0],
                        $unit[$uk][1],
                        $unit[$uk][2]
                    );
                }
            }
        } else {
            $out[] = $nul;
        }
        $out[] = $this->morph(
            (int) $rub,
            $unit[1][0],
            $unit[1][1],
            $unit[1][2]
        );
        $out[] = $kop.' '.$this->morph(
            $kop,
            $unit[0][0],
            $unit[0][1],
            $unit[0][2]
        );

        return trim(preg_replace('/ {2,}/', ' ', implode(' ', $out)));
    }

    /**
     * Склоняем словоформу
     */
    public function morph($n, $f1, $f2, $f5)
    {
        $n = abs($n) % 100;
        if ($n > 10 && $n < 20) {
            return $f5;
        }
        $n %= 10;
        if ($n > 1 && $n < 5) {
            return $f2;
        }
        if ($n === 1) {
            return $f1;
        }

        return $f5;
    }
}

@bupy7
Copy link
Author

bupy7 commented Nov 20, 2021

https://www.php.net/manual/en/class.numberformatter.php

$value = 123.45;
$intPart = sprintf('%d', $value);
$fractionalPart = round(($value - $intPart) * 100);
echo (new NumberFormatter('ru-RU', NumberFormatter::SPELLOUT))->format($intPart);
echo ' руб. ';
echo (new NumberFormatter('ru-RU', NumberFormatter::SPELLOUT))->format($fractionalPart);
echo ' коп.';

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment