Come gestire i valori monetari in PHP e MySql?

16

Ho ereditato un enorme mucchio di codice legacy scritto in PHP su un database MySQL. La cosa che ho notato è che l'applicazione utilizza doubles per la memorizzazione e la manipolazione dei dati.

Ora mi sono imbattuto in numerosi post che menzionano come double non sia adatto per le operazioni monetarie a causa degli errori di arrotondamento. Tuttavia, non ho ancora trovato una soluzione completa su come i valori monetari dovrebbero essere gestiti nel codice PHP e memorizzati in un database MySQL.

C'è una buona pratica quando si tratta di gestire denaro specificamente in PHP?

Le cose che sto cercando sono:

  1. Come devono essere memorizzati i dati nel database? tipo di colonna? dimensioni?
  2. Come devono essere gestiti i dati in normali addizioni, sottrazioni. moltiplicazione o divisione?
  3. Quando devo arrotondare i valori? Quanti arrotondamenti sono accettabili se ce ne sono?
  4. C'è una differenza tra la gestione di grandi valori monetari e valori bassi?

Nota: Un codice di esempio semplificato MOLTO di come potrei incontrare i valori monetari nella vita di tutti i giorni (vari problemi di sicurezza sono stati ignorati per semplificazione.) Naturalmente nella vita reale non userei mai il mio codice in questo modo:

$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?

Spero che tu usi questo codice di esempio come mezzo per tirar fuori più casi d'uso e non prenderlo letteralmente, ovviamente.

Domanda bonus Se devo usare un ORM come Doctrine o PROPEL, quanto sarà diverso usare i soldi nel mio codice.

    
posta Songo 29.10.2012 - 19:55
fonte

3 risposte

6

Può essere abbastanza complicato gestire i numeri con PHP / MySQL. Se si utilizza decimale (10,2) e il numero è più lungo o ha una precisione maggiore verrà troncato senza errori (a meno che non si imposti la modalità corretta per il server db).

Per gestire valori elevati o valori di alta precisione è possibile utilizzare la libreria come BCMath consentirà per eseguire operazioni di base sui numeri grandi e mantenere la precisione richiesta.

Non sono sicuro di quali calcoli farai, ma devi anche tenere presente che (0,22 * 0,4576) + (0,78 * 0,4576) non sarà uguale a 0,4576 se non utilizzerai la precisione corretta attraverso il processo.

La dimensione massima di DECIMAL in MySQL è 65, quindi dovrebbe essere più che sufficiente per qualsiasi scopo. Se si utilizza il tipo di campo DECIMAL, verrà restituito come stringa indipendentemente dall'utilizzo di un ORM o semplicemente PDO / mysql (i).

How should the data be stored in the database? column type? size?

DECIMAL con precisione di cui hai bisogno. Se utilizzi i tassi di cambio, avrai bisogno di almeno quattro cifre decimali

How should the data be handling in normal addition, subtraction. multiplication or division?

Usa BCMath per essere salvato e perché utilizzare float potrebbe non essere una buona idea

When should I round the values? How much rounding is acceptable if any?

Per valori monetari normali sono accettabili due cifre decimali, ma potrebbe essere necessario un importo maggiore se ad esempio si utilizzano i tassi di cambio.

Is there a difference between handling large monetary values and low ones?

Dipende da cosa intendi per grande. C'è sicuramente una differenza tra la gestione dei numeri con alta precisione.

    
risposta data 17.11.2012 - 02:10
fonte
9

Un semplice lavoro è archiviarli come numeri interi. 99,99 memorizzati come 9999. Se questo non funziona (e ci sono molte ragioni per cui questa potrebbe essere una cattiva scelta) è possibile utilizzare il tipo Decimale. link sul lato mysql. Dal lato php ho trovato questo link che potrebbe essere quello che stai cercando.

Domanda bonus: difficile da dire. L'orm funzionerà in base ai tipi di dati scelti. Direi che potresti fare qualcosa con l'astrazione per aiutare, ma questo specifico problema non viene affrontato semplicemente spostandoti su un ORM.

    
risposta data 29.10.2012 - 20:13
fonte
3

Cercherò di mettere la mia esperienza in questo:

Things I'm looking for are:

How should the data be stored in the database? column type? size?

Ho usato DECIMAL(10,2) per mysql senza problemi (8 completano e 2 decimali == 99.999.999,99 == quantità enorme), ma questo dipende dalla gamma di soldi che dovrai coprire. Grandi quantità dovrebbero essere prese con estrema cura (valori massimi del SO per esempio). Sulla parte decimale utilizzo 2 valori per evitare valori troncati o di arrotondamento. Prendendo i soldi ci sono pochi casi in cui hai bisogno di più decimali (nel qual caso devi assicurarti che l'utente lavori con tutti loro, altrimenti sono dati inutili)

How should the data be handling in normal addition, subtraction. multiplication or division?

Lavorare con una valuta e una tabella di scambio (con date). In questo modo ti assicurerai di aver sempre salvato l'importo corretto. Un extra: salva i valori completi e crea una vista con i risultati del calcolo. Questo ti aiuterà a correggere i valori al volo

When should I round the values? How much rounding is acceptable if any?

Di nuovo, dipende dalla gamma di denaro del tuo sistema. Pensa sempre ai termini KISS a meno che non sia necessario cadere nel caos dello scambio di valuta

Is there a difference between handling large monetary values and low ones?

A seconda del tuo sistema operativo e dei linguaggi di programmazione, devi sempre controllare i valori massimo e minimo

    
risposta data 29.10.2012 - 23:54
fonte

Leggi altre domande sui tag