Magento 1.x on PHP 7.x – JSON and performance

Continuing on the work of the Inchoo Company found here I decided to dive into the JSON errors of PHP 7 and up which this extension doesn’t address. As of PHP 7 errors are thrown where warning occurred in PHP 5.6 and below. Since a lot of extensions contain “faulty” formated json strings a lot of errors will be thrown which will break your Magento instance. Since Magento is Based on the Zend framework it contains the class Zend_Json to handle all its json decoding which includes a Zend version of the PHP json_decode function (which now throws the errors).

 

Solution

By letting the Zend_Json class know we want to use the Zend function in stead of the PHP built-in function we fix most of the problems and do have a quick fix. The only thing needed is to edit/reset a variable in the Zend_Json class from False to True, namely $useBuiltinEncoderDecoder. This does come with a price performance wise but either way it makes Magento work again without to much of a struggle.

/**
 * @var bool
 */
public static $useBuiltinEncoderDecoder = false;

 

Performance

Since we upgraded to a new PHP version not just to be more secure but also to improve performance I benchmarked the Zend_Json class to see just how much of an impact this would be.

 

Benchmark code

The code is done quick and dirty, no need for a lot of hassle in this case since this will never be used in production or anything even close to it. I downloaded a bunch of version of Magento, placed them all in a folder named Magento and loop through it.

<?php

// I hate typing this over and over again
define("DS", constant('DIRECTORY_SEPARATOR'));

function parse_args() {
    return getopt("f::");
}

function list_dir() {
    $dir = new DirectoryIterator(dirname(__FILE__) . DS . 'magento');
    $results = [];
    foreach ($dir as $entry) {
        if (!$entry->isDot() && is_dir($entry->getPathName())) {
            $results[] = $entry->getFilename();
        }
    }
    return $results;
}

$json_string = '{"sid":"kpbt3ucg6oivtb5robeknethg6","blocks":{"topLinks":"SUPER_SECRET"},"prices":{"c_0":"test1","c_1":"test2"},"formkey":"mhnfXWEm1OpGMHEl"}';

function test($version) {
    $path = dirname(__FILE__) . DS . 'magento'.DS . $version . DS .'lib'.DS.'Zend'.DS;
    include_once($path . DS . 'Json.php');
    include_once($path . DS . 'Json'.DS.'Expr.php');
    include_once($path . DS . 'Json'.DS.'Decoder.php');
    include_once($path . DS . 'Json'.DS.'Encoder.php');

    $n = 10000;
    Zend_Json::$useBuiltinEncoderDecoder = false;

    $start_time = microtime(TRUE);
    global $json_string;
    for($i=0;$i<$n;$i++)
        Zend_Json::decode($json_string);
    $end_time = microtime(TRUE);

    $default = $end_time  - $start_time;

    $start_time = microtime(TRUE);
    Zend_Json::$useBuiltinEncoderDecoder = true;
    for($i=0;$i<$n;$i++)
        Zend_Json::decode($json_string);
    $end_time = microtime(TRUE);

    return array($default/$n, ($end_time- $start_time)/$n);
}

$args = parse_args();
if(empty($args)) {
    $results = list_dir();
    sort($results);

    $php_versions = array(
        '/usr/bin/php5.5',
        '/usr/bin/php5.6',
        '/usr/bin/php7.0',
        '/usr/bin/php7.1',
    );

    foreach($php_versions as $PHP) {
        foreach ($results as $result) {
            $current_dir = dirname(__FILE__);
            $line = "$PHP $current_dir/test.php -f=$result";
            echo($PHP. ', ' .exec($line) . PHP_EOL);
        }
    }
} else {
    $version = $args['f'];
    list($default, $buildIn) = test($version);
    echo($version . ', ');
    echo(number_format($default, 10) . ', ');
    echo(number_format($buildIn, 10));
}

 

Results

As we can see below, the Magento version doesn’t really matter but which function you use does. Now although Zend_Json indeed may be 40 to 50 times slower, keep in mind we’re talking about micro seconds!

Zend decode versions

Now let’s compare it over PHP versions, we can see that since PHP 7 and up the performance is way better. From about 220 to 75 microseconds.

 

PHP versions

Results raw

php5.5, magento-1.5.1.0, 0.0000062235, 0.0002391300
php5.5, magento-1.6.2.0, 0.0000059347, 0.0002236235
php5.5, magento-1.7.0.2, 0.0000059860, 0.0002317647
php5.5, magento-1.8.1.0, 0.0000057027, 0.0002528994
php5.5, magento-1.9.0.0, 0.0000054523, 0.0002441110
php5.5, magento-1.9.0.1, 0.0000056044, 0.0002247788
php5.5, magento-1.9.1.0, 0.0000064057, 0.0002225290
php5.5, magento-1.9.2.2, 0.0000056842, 0.0002255461
php5.5, magento-1.9.2.3, 0.0000057778, 0.0002257549
php5.5, magento-1.9.2.4, 0.0000060367, 0.0002244922
php5.5, magento-1.9.3.0, 0.0000059310, 0.0002223709
php5.6, magento-1.5.1.0, 0.0000031908, 0.0002100917
php5.6, magento-1.6.2.0, 0.0000036773, 0.0002234595
php5.6, magento-1.7.0.2, 0.0000061952, 0.0002152250
php5.6, magento-1.8.1.0, 0.0000042494, 0.0002124001
php5.6, magento-1.9.0.0, 0.0000052655, 0.0002112230
php5.6, magento-1.9.0.1, 0.0000032238, 0.0002211191
php5.6, magento-1.9.1.0, 0.0000053069, 0.0002129435
php5.6, magento-1.9.2.2, 0.0000060306, 0.0002230750
php5.6, magento-1.9.2.3, 0.0000050849, 0.0002219742
php5.6, magento-1.9.2.4, 0.0000032661, 0.0002148725
php5.6, magento-1.9.3.0, 0.0000052958, 0.0002308059
php7.0, magento-1.5.1.0, 0.0000028581, 0.0000791493
php7.0, magento-1.6.2.0, 0.0000020628, 0.0000852102
php7.0, magento-1.7.0.2, 0.0000033739, 0.0000853411
php7.0, magento-1.8.1.0, 0.0000027138, 0.0000786556
php7.0, magento-1.9.0.0, 0.0000034897, 0.0000794563
php7.0, magento-1.9.0.1, 0.0000034709, 0.0000793520
php7.0, magento-1.9.1.0, 0.0000022498, 0.0000804984
php7.0, magento-1.9.2.2, 0.0000033906, 0.0000810149
php7.0, magento-1.9.2.3, 0.0000031476, 0.0000789370
php7.0, magento-1.9.2.4, 0.0000035517, 0.0000798687
php7.0, magento-1.9.3.0, 0.0000031868, 0.0000791876
php7.1, magento-1.5.1.0, 0.0000033674, 0.0000815246
php7.1, magento-1.6.2.0, 0.0000018148, 0.0000779472
php7.1, magento-1.7.0.2, 0.0000047694, 0.0000794089
php7.1, magento-1.8.1.0, 0.0000018338, 0.0000938341
php7.1, magento-1.9.0.0, 0.0000018877, 0.0000780638
php7.1, magento-1.9.0.1, 0.0000029819, 0.0000804598
php7.1, magento-1.9.1.0, 0.0000032255, 0.0000795736
php7.1, magento-1.9.2.2, 0.0000027295, 0.0000843982
php7.1, magento-1.9.2.3, 0.0000036492, 0.0000804254
php7.1, magento-1.9.2.4, 0.0000017789, 0.0000783246
php7.1, magento-1.9.3.0, 0.0000018967, 0.0000769224

 

Conclusion

Should you ever run into the json decode errors, simply enable the Zend decode function as described above and add a “fix json syntax” ticket somewhere (at random) in the ToDo pile. The ~ 80 microseconds vs the ~ 5 microseconds aren’t going to impact your performance that much and compared to the work that goes into fixing this it isn’t worth it.