Digging Magento 2 — Random Math

Matt Chad
3 min readDec 24, 2020
A city that has a typo in it’s name

Your client requested you to store invoices as PDFs. Customers, especially guests, must have a way to access their invoices without viewing My Account dashboard. Links protected from traversing. This is a perfect case for hashes as identifiers for the requested invoice.

Random

Source: https://xkcd.com/221/

You might be tempted to use:

srand((int)((double)microtime() * 1000000));
$someRandomHash = md5(uniqid((string)rand()));

And you will get the expected results every time. (Unless you ran out of luck and generated hashes will fall into this 1.47*10–29 collision chance)

Let’s dig into Magento’s Framework directory and see what’s under the hood.

\Magento\Framework\Math\Random::getUniqueHash

Perfect! It is a rare chance for us to reuse code written by someone else.

One might ask. Why introduce another dependency? If it works, why bother?

The answer is straightforward. One day implementation of random code generation might change. Who knows. Now PHP uses 32-bit version of Mersene Twister random number generator which is good, but not perfect. And this notation is neat and readable. Moreover, you do write code easily understandable. Code for other developers, not just for yourself — don’t you?

We’re reusing a class good at one and only task — random value generation. I’m glad that they’ve named it Math\Random, not Math\Helper.

You can generate an unique string and a single number as well:

\Magento\Framework\Math\Random::getRandomString
\Magento\Framework\Math\Random::getRandomNumber

Random string can be generated from custom characters or by default, from all digits and ASCII letters — upper and lowercase.

Calculator

In the Magento Math module, there is another class:

Magento\Framework\Math\Calculator

Computers think in binary system. It is hard for them to store floating point numbers precisely. In PHP float is implemented as IEEE 754 typically. Representation depends on the system on which a PHP interpreter is running.

Adding two non-integer numbers, and multiplying them by 10:

var_dump((0.1+0.7)*10);
float(8)

We’ve got an expected number 8. Rounding now this number should return 8:

var_dump(floor((0.1+0.7)*10));
float(7)

What’s going on here? Internal representation of 0.1+0.7 results in something like 7.9999999999999991118…, number-almost-eight. Printing function var_dump is smart and tricks us by rounding 7.999… into 8. However, floor is very strict and treats values seriously. That is the reason why the floor function takes number-almost-eight to the ground.

Magento uses epsilons and magic math to cut off accumulated errors after rounding. I wonder why it’s not yet changed to BCMath.

The best approach to manipulate money would be with a special, money type. PHP has a library for safe play with money: https://github.com/moneyphp/money

Now we’re protected against the unwanted case where Zimbabwean dollars are transferred to an account for US dollars — at the same, 1:1 exchange rate. Thank’s Frederik!

Although nothing wrong has been reported on websites running on Magento 2 in production, it is still a high risk gambling to not use any battle-tested money library. High risk, as for an e-commerce platform with rich features and a vast amount of third party modules.

--

--