PHP 7.1 pitfalls for Logic Hooks

While we introduced PHP 7.1 support in the Sugar 7.9.0 release back in May 2017, we have up until now continued to support PHP 5.6. But PHP 5’s days are numbered. We will be dropping support for PHP 5.6 in the Sugar 8 and Spring ‘18 releases. Sugar 8.0.0 and Spring ‘18 will only support PHP 7.1. Long live PHP 7!

In the Sugar cloud, we have been migrating from PHP 5.6 to PHP 7.1. With this transition, we have discovered some additional PHP 7 pitfalls that we want to make sure everyone avoids in their Sugar customizations. These problems affect any version of Sugar that uses PHP 7. They are both centered around a new ArgumentCountError that was introduced in PHP 7.1.

If you haven’t already, you should check out the old blog post we did on preparing customizations for PHP 7. There are lots of benefits for using PHP 7 with Sugar. Not only are there performance improvements but Sugar code as well as customizations are now able to take advantage of more advanced PHP language features.

Let’s take a look at the two pitfalls we want to help you avoid.

Do not use deprecated PHP 4 style constructors in Logic Hooks classes

You should not be using PHP 4 style constructors in PHP 7 at all, but there is an additional complication that is introduced when you are using Logic Hooks with PHP 7.1.

Take the following example:

class Hook {
    public function hook($bean, $event, $arguments)
    {

    }
}

The issue here is rooted in the fact that functions and classes in PHP are case insensitive.  The hook() function contained within the Hook class is interpreted as a PHP 4 style constructor even though the names differ in case.

For logic hook code where the class name and function name differ only in case, it would cause Sugar to throw fatal PHP errors in PHP 7.1 and warnings in earlier PHP versions. Since the class name and function name does not match in the example, Sugar will not recognize hook() as a constructor. So Sugar will first instantiate the Hook object and then subsequently call the hook() function later. But PHP recognizes hook() as a constructor. So PHP would instantiate a Hook object without passing the needed arguments to hook(). Calling a function with an incorrect number of arguments in PHP 7.x throws a fatal ArgumentCountError.

When using PHP 5 with Sugar, this would generate a PHP warning and ultimately the hook() function would be called twice; once with no arguments for the constructor and then a second time with arguments to process the hook.

So there’s all kinds of things going wrong here in this very brief example.

For compatibility reasons, we have fixed this case sensitive behavior where we will only call this function once in Sugar 8.0.0 but even then you are still using a deprecated PHP 4 constructor in PHP 7. This is either unintentional or simply a bad idea.

You must use function names that are different than your class names in your Logic Hook classes.

We can easily fix the example above by changing the function name.

class Hook {
    public function afterSave($bean, $event, $arguments)
    {

    }
}

Do not define too many arguments for Logic Hooks functions

Now we should take a look at another issue that will throw an ArgumentCountError.

class Hook {
    public function afterSave($bean, $event, $arguments, $customArgs)
    {

    }
}

The developer who wrote the afterSave() function above may have been trying to save time by adding the $customArgs argument to a standard after_save module logic hook function to handle some alternative usages. The issue is that Sugar is going to call this logic hook function with three arguments instead of four, which will throw an ArgumentCountError in PHP 7.1. In PHP 5, a warning is simply logged and the function is still executed.

It should go without saying that functions should be called with the correct number of arguments.

¯\_(ツ)_/¯

We can fix the example above to ensure that functions can always be called with the correct number of arguments.

class Hook {

    public function afterSave($bean, $event, $arguments)
    {
        $this->otherSave($bean, $event, $arguments, array());
    }

    public function otherSave($bean, $event, $arguments, $customArgs)
    {

    }

}

You must ensure that your Logic Hook functions use the correct number of arguments.

For most logic hook events that is three arguments but sometimes it is two! Check the event definitions in the Logic Hooks section of the Developer Guide if you aren’t sure how many arguments are required!

Recap

Let’s recap what you should do to prepare your Logic Hooks for PHP 7.1:

  • Ensure that none of your Logic Hook classes contain functions have the same name as the class. Remember that PHP is case insensitive when it comes to class and function names.
  • Ensure that your Logic Hook functions use the correct number of arguments.