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 messages mentionnant comment double
ne conviennent pas 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 lorsqu'il s'agit de gérer de l'argent spécifiquement en PHP?
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?
- Comment les données doivent-elles être gérées en addition, soustraction normales? multiplication ou division?
- Quand dois-je arrondir les valeurs? Combien d'arrondi est acceptable le cas échéant?
- Existe-t-il une différence entre la gestion de valeurs monétaires élevées et de valeurs faibles?
Remarque: Un exemple de code TRÈS 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 la simplification. Bien sûr, dans la vraie vie, je n'utiliserais jamais mon code comme celui-ci):
$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 utilisez 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.
decimal
type en C # a une précision limitée mais convient parfaitement aux valeurs monétaires.Réponses:
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).
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
Utilisez BCMath pour être du côté de la sauvegarde et pourquoi l'utilisation de float peut ne pas être une bonne idée
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.
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.
la source
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 du côté mysql. Du côté php, j'ai trouvé ce /programming/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.
la source
Je vais essayer de mettre mon expérience dans ceci:
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)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
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
Selon votre système d'exploitation et vos langages de programmation, vous devez toujours vérifier vos valeurs max et min
la source
$valueToBeStored= $a * $b;
if$a
et que$b
les deux sont lus comme des décimales de la base de données, je pense qu'ils seront convertisdouble
en PHP, n'est-ce pas? cela affectera-t-il les chiffres?$a
et$b
sont tirés de db? ainsi, dans mon exemple, vous n'avez jamais besoin de stocker$valueToBeStored
car vous aurez toujours la source$a
et les$b
données. Ainsi, vous pouvez travailler par programmation la valeur dans une fonction ou ainsi ou créer une vue mysql avec le résultat de la colonne. De cette façon, si une valeur doit être changée, vous n'avez pas à vous soucier de modifier plusieurs endroits (sujet aux erreurs)