J'ai hérité d'une énorme pile de code hérité écrit en PHP au-dessus d'une base de données MySQL. Ce que j'ai remarqué, c'est que l'application utilise doubles
pour le stockage et la manipulation des données .
Maintenant, je suis tombé sur de nombreux articles mentionnant comment double
ne sont pas adaptés aux opérations monétaires en raison des erreurs d'arrondi. Cependant, je n'ai pas encore trouvé de solution complète sur la façon dont les valeurs monétaires doivent être traitées dans le code PHP et stockées dans une base de données MySQL.
Existe-t-il une meilleure pratique en matière de gestion de l'argent spécifiquement en PHP?
Les choses que je recherche sont:
Remarque: A TRÈS exemple de code simplifié de la façon dont je pourrais rencontrer des valeurs monétaires dans la vie quotidienne (divers problèmes de sécurité ont été ignorés pour simplifier. Bien sûr, dans la vraie vie, je n'utiliserais jamais mon code comme ça):
$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?
J'espère que vous utiliserez cet exemple de code comme un moyen de faire ressortir plus de cas d'utilisation et de ne pas le prendre à la lettre bien sûr.
Question bonus Si je dois utiliser un ORM tel que Doctrine ou PROPEL, à quel point sera-t-il différent d'utiliser de l'argent dans mon code.
Il peut être assez difficile de gérer les nombres avec PHP/MySQL. Si vous utilisez décimal (10,2) et que votre nombre est plus long ou plus précis, il sera tronqué sans erreur (sauf si vous définissez le mode approprié pour votre serveur db).
Pour gérer de grandes valeurs ou des valeurs de haute précision, vous pouvez utiliser une bibliothèque comme BCMath elle vous permettra d'effectuer des opérations de base sur les grands nombres et de conserver la précision requise.
Je ne sais pas exactement quels calculs vous ferez, mais vous devez également garder à l'esprit que (0,22 * 0,4576) + (0,78 * 0,4576) ne sera pas égal à 0,4576 si vous n'utilisez pas la précision appropriée tout au long du processus.
La taille maximale de DECIMAL dans MySQL est de 65 donc elle devrait être plus que suffisante pour n'importe quel but. Si vous utilisez le type de champ DECIMAL, il sera renvoyé sous forme de chaîne indépendamment de l'utilisation d'un ORM ou simplement de PDO/mysql (i).
Comment les données doivent-elles être stockées dans la base de données? type de colonne? Taille?
DECIMAL avec la précision dont vous avez besoin. Si vous utilisez des taux de change, vous aurez besoin d'au moins quatre décimales
Comment les données doivent-elles être gérées en addition, soustraction normales? multiplication ou division?
Utilisez BCMath pour être du côté de la sauvegarde et pourquoi l'utilisation de float peut ne pas être une bonne idée
Quand dois-je arrondir les valeurs? Combien d'arrondi est acceptable le cas échéant?
Pour les valeurs monétaires, deux décimales normales sont acceptables, mais vous pouvez en avoir besoin de plus si, par exemple, vous utilisez des taux de change.
Existe-t-il une différence entre la gestion de valeurs monétaires élevées et de valeurs faibles?
Cela dépend de ce que vous entendez en gros. Il y a certainement une différence entre la manipulation des nombres avec une grande précision.
Une solution simple consiste à les stocker sous forme d'entiers. 99,99 stocké en tant que 9999. Si cela ne fonctionne pas (et il y a plusieurs raisons pour lesquelles cela pourrait être un mauvais choix), vous pouvez utiliser le type Decimal. http://dev.mysql.com/doc/refman/5.0/en/precision-math-decimal-changes.html côté mysql. Du côté php, j'ai trouvé cela https://stackoverflow.com/questions/3244094/decimal-type-in-php qui pourrait être ce que vous recherchez.
Question bonus: difficile à dire. L'ORM va fonctionner en fonction des types de données choisis. Je dirais que vous pouvez faire des choses avec abstraction pour aider, mais ce problème spécifique n'est pas résolu simplement en passant à un ORM.
Je vais essayer de mettre mon expérience dans ceci:
Les choses que je recherche sont:
Comment les données doivent-elles être stockées dans la base de données? type de colonne? Taille?
J'ai utilisé DECIMAL(10,2)
pour mysql sans problème (8 complète et 2 décimales == 99.999.999,99 == énorme quantité), mais cela dépend de la fourchette d'argent que vous devrez couvrir. Des quantités énormes doivent être prises avec un soin particulier (valeurs flottantes OS max par exemple). Sur la partie décimale, j'utilise 2 valeurs pour éviter de tronquer ou d'arrondir les valeurs. En prenant de l'argent, il y a peu de cas où vous avez besoin de plus de décimales (dans ce cas, vous devez vous assurer que l'utilisateur travaillera avec tous, sinon les données sont inutiles)
Comment les données doivent-elles être gérées en addition, soustraction normales? multiplication ou division?
Travaillez avec une devise et une table de change (avec dates). De cette façon, vous vous assurez que vous aurez toujours le montant correct enregistré. Un plus: enregistrez les valeurs complètes et créez une vue avec les résultats des calculs. Cela vous aidera à fixer les valeurs à la volée
Quand dois-je arrondir les valeurs? Combien d'arrondi est acceptable le cas échéant?
Encore une fois, cela dépend de la fourchette d'argent de votre système. Pensez toujours en termes KISS sauf si vous devez tomber dans le pétrin du bureau de change
Y a-t-il une différence entre le traitement de valeurs monétaires élevées et de valeurs faibles?
Selon votre système d'exploitation et vos langages de programmation, vous devez toujours vérifier vos valeurs max et min