The Pragmatic Programmer brings up an interesting idea that I think Laravel could have learned from earlier on. Avoid inheritance and decouple components. For this article, I will specifically be discussing Laravel and PHP so the examples will of course be from that language.
I recently took up the task of updating an old Laravel app that is still in active use. The goal is to update from Laravel 4.2 to ideally at least Laravel 6.0 with plans of continuing to update it in the future (we don’t want to get that out of date again). For anyone who has tried to update a Laravel project with that big of a jump should know that this is no small task.
Initially, I used Laravel Shift to do a large bulk of the initial work to update to 5.0 since a 4.2 to 5.0 update requires a fresh install of Laravel and many tedious changes. Plus it organizes everything as pull requests so it is easy to review what needs to be done and what is left to be done. You can see an example below of the pull request Shift makes.
I then began updating the Composer dependencies but was never able to successfully install the Composer dependencies. I quickly learned that this is due to an exception handler class that was added in Laravel 5.0 that 4.2 didn’t have. This exception handler class requires that the variable being passed to the
report() method in
App\Exceptions\Handler.php to be an
Exception type. The only issue with this is that PHP 7 has changed the way errors are handled. Now the variable being passed to this method is an
Error type so the tests always fail. You can see the Laravel code here: https://github.com/laravel/laravel/blob/master/app/Exceptions/Handler.php.
PHP 7 changes how most errors are reported by PHP. Instead of reporting errors through the traditional error reporting mechanism used by PHP 5, most errors are now reported by throwing Error exceptions.
A fellow coworker brought up an interesting question which was, why is the current 4.2 application able to run on the server’s PHP 7.2 install? And after a quick investigation, I was able to see that this exception handling was a new feature added to Laravel 5.0; so it never needed to pass the specific tests that use the exception handlers when running
While it’s impossible to have foreseen the future that PHP 7 created for error handling, the problem could have potentially been solved had the developers used an interface as opposed to inheritance.
By using an interface all that is required when I extend the class is that I use the method. This would allow me to modify it as needed so that I can pass it any type I wish. By inheriting the
ExceptionHandler class, I am now coupled to “vendor” code. We can take it a step further by claiming that this also forces the use of an
Exception type so the code is now coupled to the
Exception class. We are coupled in two places that shouldn’t be coupled! Especially if the tests require to even run the project are run through this code that I cannot change!
My only options at this point are to make another jump to Laravel 5.1 or to run a virtualized environment that can run PHP 5.4 or 5.6. The former I don’t feel comfortable doing without running a significant number of tests first. The application is essentially 5 years old so who knows what lies in the depths waiting to be discovered. The latter is a big-time suck that will require what could have been an unavoidable effort if the developers had just done some simple decoupling.
TL;DR: Laravel 4.2 and 5.1 are compatible with PHP 7, but miraculously Laravel 5.0 is not. Gee, I wonder if we could have solved this by some simple decoupling?