- Pestle 1.1.1 Released
- Pestle 1.1.2 Released
- Magento 2 Setup Migration Scripts
- Pestle 1.2.1 Released
- Sending Text Messages with PHP, pestle, and Nexmo
- Pestle 1.3 and AbstractModel UI Generation
- Pestle 1.4.1 and the Merits of Inheritance
- Pestle 1.4.4 Released
- Pestle Docs Done (for now)
- Pestle 1.4.2 Now Available
- Installing Pestle via. Homebrew
- Pestle 1.5.2 Released
We interrupt this Modern Javascript series to let you know there’s a new release (v1.4.1) of pestle available. Pestle is both a simple PHP command line framework for sharing functions, as well as the world’s preeminent collection of Magento 2 code generation routines. It’s an integral part of our PHP for MVC Developers series, and the missing toolkit for working programmers wrestling with Magento 2.
You can find details on the 12 issues solved in 1.4.1 release over in the closed GitHub Milestone list. This release was a combination of some low hanging fruit, some wrapper/helper commands for the various Magento migration tools, and a new command for easy constructor copying in M2. We’ll discuss the later today, but keep your eyes on Magento Quickies in the coming days/weeks for some examples of the migration helper commands.
Existing users can run
$ pestle.phar selfupdate
to grab the latest version. New users can find more information in the GitHub README.
Constructor Inheritance
The stand out command in this release is magento2:generate:class-child
. This command will generate a new PHP class that inherits from an existing class and it copies the closest constructor in the inheritance chain. Running the following command
pestle.phar magento2:generate:class-child "Pulsestorm\Helloworld\Block\Template" "Magento\Framework\View\Element\Template"
will generate the following class.
<?php
namespace Pulsestorm\Helloworld\Block;
class Template extends \Magento\Framework\View\Element\Template
{
function __construct(Template\Context $context, array $data = [])
{
parent::__construct($context,$data);
}
}
That is — we have our own class that extends Magento’s Magento\Framework\View\Element\Template
class, with a compatible constructor. We’re now free to inject dependencies as needed.
One of the more annoying parts of Magento 2 is suddenly realizing you need a new dependency in your class and having to dig back through the core source code for a parent’s constructor in order to grab all of its dependencies. With magento2:generate:class-child
, you can easily generate constructor parameters that are identical to those in the parent class, and then start adding your own.
For folks reading some of the surface level commentary around M2 it may seem strange that I’ve spent time on a tool that assumes the importance of class inheritance. Magento Inc’s been making a lot of noise recently about moving towards everything private
/final
by default and adopting patterns that (in theory) prefer class composition over inheritance.
However, as a working Magento developer, these choices seem — strange at best. It’s not that there’s not sound software architecture thinking behind them, it’s that those architecture principles seem misplaced for the code base we have, as well as for the sorts of things Magento’s been traditionally good for.
The Case Against Inheritance
It’s probably a good idea to start with the case against inheritance. Lets consider the above example
<?php
namespace Pulsestorm\Helloworld\Block;
class Template extends \Magento\Framework\View\Element\Template
{
function __construct(Template\Context $context, array $data = [])
{
parent::__construct($context,$data);
}
}
Here we’ve extended Magento’s base template class. This means my template gets access to all the public
and protected
methods inside of Magento\Framework\View\Element\Template
. This is the power of inheritance. I have a new class type that will behave the same as previous class types. Anywhere Magento core code uses a Magento\Framework\View\Element\Template
object, I can use my class without needing to write a single additional line of code. If I want to do something that Magento\Framework\View\Element\Template
objects already do, I get to do it without writing a single additional line of code (except, of course, the code that calls those base class methods)
The problem with this? The developer responsible for maintaining Magento\Framework\View\Element\Template
now needs to make sure that any changes they make are compatible with what I’ve done. This means every public
and protected
method needs to act the same when used with any inputs, and that the state of the public
and protected
properties needs to remain the same when those public
and protected
methods manipulate that state.
This can be challenging enough when everyone works inside the same building and draws a paycheck from the same organization. Add the challenge of a distributed work force, with often unclear/murky business/employment relationships around all parties? Under those sorts of circumstances it can be a maddening, seemingly impossible affair.
One solution for the maintainer of Magento\Framework\View\Element\Template
? Discourage inheritance. Make all methods and data properties private
(save one public
method), mark your classes as final
, and tell developers they should instantiate a new instance of your class and use the object instead of inheriting from its class. This is composition. We, as third party developers, use objects provided by the system owner to do things.
The maintainer of this theoretical Magento\Framework\View\Element\Template
class still needs to make sure their one public
method works the same from version to version, but they’re free to change any implementation detail they need to, because all those implementation details are in private
methods that only the class itself can call.
The Problem with Composition
Class composition is a fine architecture approach. However — it’s not without tradeoffs. It reduces the ability of the third party developers (or first party team members of the Magento\Framework\View\Element\Template
maintainer) from re-using any code in those private
methods. It operates from an assumption that either the system owner will be able to anticipate everything a third party developer needs, or that the third party team has the resources and bandwidth to write this functionality for themselves.
In an open source project, the practical effect is resource strapped third party developers will copy and paste those private
methods into their own classes. This makes for massive code duplication, and it means when that code no longer works in a future version (which remember, is the point of private
methods), the third party code will no longer work.
In an open source system, the practical effect of shifting to class composition is the burden of maintenance shifts from the system owner to the third party developers using the system. In a world of infinite resources this wouldn’t be a problem. If any of you have discovered this world, please get in touch with your literature.
Inheritance, Class Composition, and Magento 2
We have one last wrinkle to consider with Magento 2. Magento 2 is, still, a PHP MVC framework. Web oriented MVC frameworks have, for over a decade, worked with an inheritance centric world view. Consider what I said above
One solution for the maintainer of
Magento\Framework\View\Element\Template
? Discourage inheritance. Make all methods and data propertiesprivate
(save onepublic
method), mark your classes asfinal
, and tell developers they should instantiate a new instance of your class and use the object instead of inheriting from its class
This probably caused a lot of cognitive dissonance for Magento developers. The whole point of Magento’s block hierarchy is you inherit from a block, gets its powers, and make small changes to its functionality.
The same holds true for Magento’s (or any PHP MVC system’s) controller classes. Want a page that acts like the Catalog listing page? Create a new controller that inherits from the old one, and make small changes to enable your new functionality.
Putting aside the whole first-party/third-party dynamic, trying to shift Magento 2’s codebase over to a methodology that favors composition over inheritance seems like a boil-the-ocean sort of problem that will, at best, meet with mixed and tepid results.
Practical Steps Forward
So, for a working Magento developer? We’re stuck in a world where we need to rely on inheritance techniques in a codebase that’s becoming increasingly hostile towards them. This is where commands like magento2:generate:class-child
can offer you small time savings everyday which will add up to overall improved productivity. I’m knee deep in a number of migration projects and we’re often dealing with blocks that have a significant number of dependencies.
<?php
namespace Pulsestorm\Helloworld\Block;
class Page extends \Magento\Cms\Block\Page
{
function __construct(
\Magento\Framework\View\Element\Context $context,
\Magento\Cms\Model\Page $page,
\Magento\Cms\Model\Template\FilterProvider $filterProvider,
\Magento\Store\Model\StoreManagerInterface $storeManager,
\Magento\Cms\Model\PageFactory $pageFactory,
\Magento\Framework\View\Page\Config $pageConfig,
array $data = []
) {
parent::__construct($context,$page,$filterProvider,$storeManager,$pageFactory,$pageConfig,$data);
}
}
Not needing to manually copy these constructor parameters from the parent class and then pull together a parent::__construct
parameter string may seem like a small thing — but it’s a fiddly and error prone process that can add hours to a project over a week. Like so many developer tools, magento2:generate:class-child
does something tedious for us so we can focus our limited attention elsewhere.