Periodically, I do code reviews of other extensions, for use in training or just for fun. One extension bumped up on my radar after I asked on the former Dutchento Slack about bad modules. I did a quick review and the amount of bad code was staggering. Even worse, this extension was listed on the Magento Marketplace. This blog lists all points I found for the purpose of spreading better practices with others.

Baseline

Writing a good third-party extension for Magento 2 is challenging at times. It requires a sharp sight, a fair amount of common sense and advanced knowledge. While coding standards like PSR-2 only set the baseline, Magento also has its own additional rules and practices. It is a lot to digest.

However, a third-party extension is spread across numerous places. Not sticking to the coding standards potentially leads to extension conflicts, more costs for modification and more time spent on hard-to-trace bugs. A third-party extension developer, therefore, should set higher requirements to her or his code then an in-house developer should.

Using Helper classes

Here we go. The extension was making use of Helper classes, while in general, Helper classes are nowadays frowned upon. They are often created when the creator doesn't have enough fantasy about what else to name the class. I have to admit, some of my own extensions still contain helpers, but refactoring should get rid of them.

Too much logic in PHTML templates

There was too much going in PHTML templates. Collection filters were added, the ObjectManager was called upon (which I will address in a later point separately) and in a lot of places, the PHP code outnumbered the HTML code.

A template should only be about templating. Ideally, the template only calls upon a Block to get something ($block->getFoobar()) or perhaps a ViewModel is used for this ($viewModel->getFoobar()). Any advanced logic (apart from ifs, loops and simple output) should be moved to the underlying classes (Block or ViewModel).

Code was not cleaned up

There were numerous classes that were created but never used. There were also classes that were used while only extending a parent class and not adding anything on top of it - the parent should be used instead.

There were events listed to using observers while the observers didn't do anything. There was even a pointless PHP class in there with broken PHP code, so that linting (php -l) of the extension folder failed. Sometimes dependencies were injected in the constructor, but not used at all. There were numerous namespaced classes imported in the top (use A) but not used in the code - it popped up in my PHPStorm editor right away.

Undocumented requirements

The composer.json and module.xml were lacking in documenting the extension its dependencies. There were dependencies with PHP 5.5, while I don't recall that Magento 2 was ever compatible with that PHP version. But the Magento Framework itself and some other modules (Magento_Catalog and Magento_Checkout) were missing from the require section in composer. And the modules were not listed in the sequence section of the etc/module.xml file.

Personally, I'm using my own Yireo_ExtensionChecker a lot to detect what the requirements are.

Coding standard stuff

The code was definitely not compliant with the official Magento Coding Standard (which is kind of new, but really useful). There were warnings in there with up to severity 9. The code was also far from compliant with PSR-1 or PSR-2, which basically showed that the creators never bothered to use simple formatting by their IDE.

There were silly things in there. Prefixing internal variables with an underscore - well, the Magento core does so itself, even though it pops up with PSR-2 scans, but this is just a small little thing. There were also long methods in the code (largest method had about 400 lines of code) with far too many features so that the code was definitely not SOLID. Also, there were frequently too many indents in the code, for instance up to 12 indents in a single class, which shows that applying the Object Calisthenics could definitely benefit here.

Yes, the ObjectManager, always the ObjectManager

When the ObjectManager is used in the wrong places, it is always a sign that quality is lacking. So obviously, this extension had numerous issues with it: Classes were calling upon the ObjectManager::getInstance() singleton directly, while the needed dependencies could have been injected easily in the constructor instead. This resulted in numerous parts of the code not being extendable when needed.

The ObjectManager was also called upon within PHTML templates, even though in places this was harder to detect because it was hidden way in a factory() method of the Block class. In one place, the ObjectManager was used to get() a singleton instance and then still the usage was via a custom singleton method (while actually create() should have been used).

Too many dependencies

DI was also used in the wrong way: Too many dependencies were injected, leading up to one class with 25 dependencies (only the legacy Product model outnumbers this). And I mentioned earlier already that sometimes dependencies were injected but then not used.

Preferences instead of plugins

In multiple occasions, preferences were used to rewrite original core classes, while actually the purpose of the rewrite was a perfect scenario to choose a plugin instead, or by adding a Virtual Type, or by using Type arguments, or by simply using composition. I really got the impression here that the developers didn't understand the DI toolbox that Magento gives them.

Plugins with around() instead of before() and after()

Using around() in Plugin classes is always open for debate. In the code, I found multiple instances of plugins that used around() where actually before() or after() were just as efficient.

Manual SQL code

Manual SQL queries were used to write to the Store Configuration instead of using \Magento\Framework\App\Config\Storage\WriterInterface or similar classes. There were also SQL queries in the Setup\Install class to insert default configuration values, while actually, a simple default section in etc/config.xml would have been enough.

Interestingly, there were models, resource models and collections setup. Still, manual SQL queries were used to collect and write information from the same table, while actually, the model could easily have been used. No repository.

Call to home

In the Magento Admin Panel, any time when a page was loaded, the extension kicked in by listening to the event controller_action_predispatch and calling home to its own website for a license check. This also means that if the extension vendors site or server would be down, your Magento Admin Panel would be unreachable.

The extension itself required a base module, which did a hell of a lot of work to add notifications to pop up on each backend page, in the event of a license check failing. Not nice.

No ACL file

There was no acl.xml file defined for the extensions backend pages or its configuration setting. Still, ACL resources were defined all of the place: In the necessary menu.xml, system.xml and controller classes. But there was also a custom check for an ACL resource in one controller that simply led to code that would be only executed if you were logged in with a role with access to Any resource.

CSS instead of LESS

CSS files were added manually to both the frontend and the backend. There were no LESS files involved.

Translatable templates?

In the XML layout, there was a template definition with the translate argument set to true, as if the template was translatable (<argument name="template" translate="true" xsi:type="string">sample.phtml</argument>). Either this leads to really cool behaviour (using different template files per different StoreView by using translation as a driving mechanism) or it was just a mistake.

So, why is this bad?

Overall, the extension had so many flaws that it made me nauseous. The lacking requirements in composer.json could potentially break a production site because nothing prevented the extension from being rolled out in incompatible environments. Also, the numerous bad usages of DI and the ObjectManager led to difficulties extending (or fixing) the extension properly. It was hard to create theming overrides. It added more resources on the page (internally with the call-to-home and externally via CSS) so that installing this extension, the site performance went down.

All in all, this extension lacked in code quality. But the actual problem meant that the bill went up for the merchant using this extension. Especially, when the in-house developer of this project (merchant or system integrator) would need to fix or modify things in the extension. I hope you found this (rather long) summing-up a worthy read and I hope you agree with me that extensions like these are to be avoided. I'm happy to review some more extensions.

Posted on July 3, 2019

About the author

Author Jisse Reitsma

Jisse Reitsma is the founder of Yireo, extension developer, developer trainer and 3x Magento Master. His passion is for technology and open source. And he loves talking as well.

Sponsor Yireo

Looking for a training in-house?

Let's get to it!

We don't write too commercial stuff, we focus on the technology (which we love) and we regularly come up with innovative solutions. Via our newsletter, you can keep yourself up to date on all of this coolness. Subscribing only takes seconds.

Do not miss out on what we say

This will be the most interesting spam you have ever read

We don't write too commercial stuff, we focus on the technology (which we love) and we regularly come up with innovative solutions. Via our newsletter, you can keep yourself up to date on all of this coolness. Subscribing only takes seconds.