(With apologies to whomever originally posted this technique – I can’t seem to find your original article. If you’d like link/credit just let me know)
As companies start to go ahead with Magento 2 builds and Magento 2 upgrades, more an more developers are wondering what to do about the missing code pools from Magento 1. If you’re not familiar with them, code pools allowed you to completely replace a Magento 1 class file with your own version. If you wanted to replace the Mage_Core_Model_Url
file at
app/code/core/Mage/Core/Model/Url.php
all you needed to do was put your new file in the local
code pool
app/code/local/Mage/Core/Model/Url.php
Behind the scenes, the code pool functionality was just another folder added to PHP’s include_path
.
Magento 2 did away with code pools, and this was probably the right call. Code pools created a big problem when it came time for point release upgrades. If the file in core
changed, it was up to the system owner to merge those changes into the local
version manually. Even without upgrades, it was possible to change the behavior of the class in subtle ways that might not break the pages you were working on, but would break the system when the class was used in a different context.
Despite all that – sometimes a code pool override was the only way to do what you needed to do. While code pools don’t exist in Magento 2, composer does.
Replace Class Files with Composer’s Autoloading Feature
If you’ve worked your way through my Laravel, Composer, and the State of Autoloading series, you know that most modern PHP applications use composer based autoloaders. It turns out that with some light abuse of the files
autoloader, you can replace any class definition file with one of your own. High level, all you need to do is
- Copy the original class to a different location on your system
- Add that class path to your project’s main
composer.json
file - Run
composer dumpautoload
(Composer runs this automatically whenever you runcomposer install
or `composer update)
For example, if you wanted to replace the Magento/Directory/Block/Currency
class with one of your own, all you’d need to do is copy the class to a different location on your system
$ mkdir -p app/overrides/Magento/Directory/Block/
$ cp vendor/magento/module-directory/Block/Currency.php app/overrides/Magento/Directory/Block/Currency.php
There’s nothing magic about the app/override
folder – you can place this new definition file anywhere.
Next, add that path to your project’s main composer.json
file
#File: composer.json
"autoload": {
/* ... */
"files": [
/* ... */,
"app/overrides/Magento/Directory/Block/Currency.php"
]
},
Finally, tell composer to regenerate the autoloader configuration.
$ composer dumpautoload
You’ll know its worked if you see entries for your file in the generated autoloader includes.
vendor/composer/autoload_files.php
147: '6d881679c080d9d32d99dfc737decaa4' => $baseDir . '/app/overrides/Magento/Directory/Block/Currency.php',
vendor/composer/autoload_static.php
148: '6d881679c080d9d32d99dfc737decaa4' => __DIR__ . '/../..' . '/app/overrides/Magento/Directory/Block/Currency.php',
This works because once PHP has loaded a class definition file, it will never run the standard autoload routines for that class again. Since the files
autoloader runs first, it will always win.
Is this a Good Idea?
When I first saw this technique circulating I wasn’t wild about it. While class overrides were expedient in Magento 1, they were often the cause of endless ongoing maintenance, weird bugs, and extension compatibility problems for Magento system owners.
However – even though code pool overrides are no longer a thing, Magento 2 has proven to have its own set of ongoing maintenance and extension compatibility challenges. If this technique helps your small team keep paces with a larger team of Magento developers, I’d say its worth considering the tradeoffs.