Як поводитися з грошовими значеннями в PHP та MySql?


16

Я успадкував величезну купу застарілого коду, написаного на PHP, на вершині бази даних MySQL. Я помітив, що додаток використовує doublesдля зберігання та обробки даних.

Зараз я натрапив на численні дописи, де згадували, як doubleне підходять для грошових операцій через помилки округлення. Однак я ще не зіткнувся з повним рішенням, як обробляти грошові величини в PHP-коді та зберігати в базі даних MySQL.

Чи є найкраща практика, коли мова йде про обробку грошей спеціально на PHP?

Що я шукаю:

  1. Як слід зберігати дані в базі даних? тип стовпця? розмір?
  2. Як слід обробляти дані в звичайному складенні, відніманні. множення чи ділення?
  3. Коли слід округлювати значення? Наскільки допустиме округлення, якщо воно є?
  4. Чи є різниця між обробкою великих грошових цінностей і низьких?

Примітка: дуже спрощений приклад коду , як я міг би зіткнутися цінності грошей в повсякденному житті (різні питання безпеки були проігноровані для спрощення Звичайно , в реальному житті я ніколи б не використовувати мій код , як це.):

$a= $_POST['price_in_dollars']; //-->(ex: 25.06) will be read as a string should it be cast to double?
$b= $_POST['discount_rate'];//-->(ex: 0.35) value will always be less than 1
$valueToBeStored= $a * $b; //--> any hint here is welcomed 

$valueFromDatabase= $row['price']; //--> price column in database could be double, decimal,...etc.

$priceToPrint=$valueFromDatabase * 0.25; //again cast needed or not?

Я сподіваюся, що ви використовуєте цей зразок коду як засіб, щоб виявити більше випадків використання, а не, звичайно, приймати його.

Питання про бонус Якщо я буду використовувати ORM, наприклад, доктрину чи PROPEL, наскільки різним буде використання грошей у моєму коді.


1
Я не знаю PHP, але знання термінології є неоціненним для цих сценаріїв для google-fu, термін, який ви шукаєте, - "довільна точність", на яку google відповідає php.net/manual/en/book.bc.php
Джиммі Хоффа

1
@ Джіммі Хоффа: довільна точність, як правило, не те, що потрібно, те, що може точно представляти і працювати з десятковими дробами. Звичайно, ви часто зустрічаєте обидва комбіновані, але, наприклад, decimalтип C # має обмежену точність, але цілком підходить для грошових значень.
Майкл Боргвардт

1
@MichaelBorgwardt правильно, я забуваю про раціональні типи, тому що так багато мов їх не має. Гарний дзвінок.
Джиммі Хоффа

Працюючи багато з валютами та застарілим кодом, я б сказав, щоб зберегти його в цілих числах і використовувати центи замість доларів. Однак будьте обережні, 32 цілі числа можуть містити напрочуд невелику кількість.
Пітер Б

На відміну від вашого зразкового коду, який показує ціни та знижки, які розміщені, мене страшить. Сподіваємось, це настільки спрощено, що не відображає реального використання, але за номіналом, схоже, ви довіряєте браузеру, щоб повідомити, яка ціна товару, відправивши назад приховане поле чи якесь таке.
Carson63000

Відповіді:


6

Обробляти номери за допомогою PHP / MySQL може бути досить складно. Якщо ви використовуєте десятковий (10,2) і ваш номер довший або має більш високу точність, він буде усічений без помилок (якщо ви не встановите належний режим для вашого сервера db).

Для обробки великих значень або значень високої точності ви можете використовувати бібліотеку типу BCMath, це дозволить вам робити основні операції на великій кількості та зберігати необхідну точність.

Я не впевнений, які саме розрахунки ви будете робити, але потрібно також пам’ятати, що (0,22 * 0,4576) + (0,78 * 0,4576) не буде рівним 0,4576, якщо ви не використовуєте належну точність в процесі.

Максимальний розмір DECIMAL в MySQL - 65, тому його повинно бути більше ніж для будь-яких цілей. Якщо ви використовуєте тип поля DECIMAL, воно буде повернуто як рядок незалежно від використання ORM або просто PDO / mysql (i).

Як слід зберігати дані в базі даних? тип стовпця? розмір?

ДЕКІМАЛ з точністю, яка вам потрібна. Якщо ви використовуєте курси валют, вам знадобиться принаймні чотири знаки після коми

Як слід обробляти дані в звичайному складенні, відніманні. множення чи ділення?

Використовуйте BCMath, щоб бути на стороні збереження, і чому використання float не може бути гарною ідеєю

Коли слід округлювати значення? Наскільки допустиме округлення, якщо воно є?

Для грошових значень прийнятні два десяткових знаки, але вам може знадобитися більше, якщо, наприклад, ви використовуєте курси валют

Чи є різниця між обробкою великих грошових цінностей і низьких?

Залежить від того, що ви маєте на увазі під великим рахунком. Однозначно є різниця між обробкою номерів з високою точністю.


9

Проста робота навколо них - зберігати їх як цілі числа. 99,99 зберігається як 9999. Якщо це не спрацює (і є багато причин, через які це може бути поганим вибором), ви можете використовувати тип Decimal. http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html з боку mysql. З боку php, я знайшов це /programming/3244094/decimal-type-in-php, яке може бути тим, що ви шукаєте.

Бонусне питання: Важко сказати. Орма буде працювати на основі вибраних типів даних. Я б сказав, що ви можете зробити деякі речі з абстракцією, щоб допомогти, але ця конкретна проблема не вирішується просто шляхом переходу до ORM.


1
FWIW, Drupal Commerce використовує трюк 9999 для зберігання своїх цін.
Флоріан Маргаїн

1
"І є багато причин, чому це може бути поганим вибором". Чи поділилися б ви деякими проблемами цієї практики?
Songo


@MichaelBorgwardt Дякую за інформацію, Сонго вибачте за затримку, ураган вивів нас :)
Омінус

3

Я спробую вкласти свій досвід у цьому:

Що я шукаю:

Як слід зберігати дані в базі даних? тип стовпця? розмір?

Я DECIMAL(10,2)без проблем використовував mysql (8 завершених і 2 десяткові дроби == 99,999,999,99 == величезна сума), але це залежить від грошового діапазону, який вам потрібно буде покрити. Величезні суми слід приймати з особливою обережністю (наприклад, максимум плаваючих значень ОС). У десятковій частині я використовую 2 значення, щоб уникнути відсікання, а також значення округлення. Щодо грошей, то мало випадків, коли вам потрібно більше десяткових знаків (у такому випадку вам потрібно переконатися, що користувач буде працювати з усіма ними, інакше це марні дані)

Як слід обробляти дані в звичайному складенні, відніманні. множення чи ділення?

Робота з однією валютою та обмінною таблицею (з датами). Таким чином ви гарантуєте, що завжди будете збережено правильну суму. Додатково: збережіть цілі значення та створіть подання з результатами розрахунку. Це допоможе вам фіксувати значення на ходу

Коли слід округлювати значення? Наскільки допустиме округлення, якщо воно є?

Знову ж таки, залежить від вашої системи грошових масивів. Завжди думайте в термінах KISS, якщо вам не потрібно впадати в обмін валюти

Чи є різниця між обробкою великих грошових цінностей і низьких?

Залежно від вашої ОС та мов програмування, вам завжди потрібно перевірити максимальні та мінімальні значення


Дякую за відповідь, але якщо мені доведеться обробити щось на зразок, $valueToBeStored= $a * $b;якщо $aі $bобидва читаються як десяткові знаки з бази даних, я думаю, що вони будуть передані doubleв PHP правильно? це вплине на числа?
Сонго

$aі $bвзяті з db? тож у моєму прикладі вам ніколи не потрібно зберігати, $valueToBeStoredоскільки ви завжди матимете джерело $aта $bдані. Таким чином, ви можете працювати програмно у значенні функції чи так, або створити подання mysql з результатом стовпця. Таким чином, якщо якесь значення потрібно змінити, вам не доведеться турбуватися про зміну декількох місць (схильних до помилок)
Alwin Kesler
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.