Refactoring Zend Framework 2.4.x for use in ZF 2.7 and 3


We’re finally working on moving to newer versions of Zend Framework, and yes, we are way way way behind on this. (But hey, it works, and we can’t keep up with all the other system updating way too fast anymore!). Once we started updating PHP to 7.2 we ran into problems because ZF 2.4.x would not run on that.

Once I started learning what to start fixing for a final upgrade move to ZF 3, I realized the way we were using the ServiceManager in ALL OUR MODULE CLASSES was now going to fail. They removed the ServiceManagerAwareInterface, which was super handy to pull in the system registry and get access to everything.

Side note: Just my thought on this, not that it matters, but forcing everyone to remove this, make their job harder and making our code more spaghetified by using factory classes to inject the SM is even worse of an anti pattern. But hey, I don’t design frameworks, I certainly don’t know what I’m talking about. It annoy’s me though. A lot!

Also, trying to follow docs and help online, everything shows that you need factory classes that utilize your actual class, even controllers, which is really confusing. They also only show examples where you load in specific objects in the factory, but I want the entire SM, so I can start coding and pull in what I want using that instead. What they don’t tell you is that you can use the factories key right in the modules.config.php with a closure, and feed in the SM on each on. This took a good day of reading and trial and error before I finally realized I could just change each invokable to a factory and pass in the SM. And then later still, I realized, “Hey Greg, why don’t you just build a little shim function to convert all those invokables to you can do less work?” And so that’s what I did. Seems to work just fine and I don’t notice any speed difference.

So here’s what I used to do in module.config.php for one of our library modules (not the main MVC app, this is a shortened version of one of many separate custom ZF modules we created called GWCore):


return [
	'service_manager' => [
		'invokables' => [
			'GWPager' => 'GWCore\GWPager',
			'GWOrderBillShip' => 'GWCore\GWOrderBillShip',
			'RetailCTL' => 'GWCore\RetailCTL',
		],
		'factories' => [
			'CobExec' => function($sm){
				$config = [
					'key' => 'something here'
				];
				return new GWCore\CobExec($sm, $config);
			},
			'Sqimp' => function($sm){
				return new GWCore\Sqimp($sm);
			},
		],
	],
];

Typical stuff. Load some custom factories and otherwise use invokables. We have many modules with 50+ classes. The thought of needing to create special factory classes is very frustrating. (and where the heck do you even put those!!?? the docs don’t say! they just show it!)

So anyway, this is just PHP, why not bend this code to my will… LOL. Here’s what I did instead.

$modInvokeFactories = [
	'GWPager' => 'GWCore\GWPager',
	'GWOrderBillShip' => 'GWCore\GWOrderBillShip',
	'RetailCTL' => 'GWCore\RetailCTL',
];

$modCustomFactories = [
	'CobExec' => function($sm){
		$config = [
			'key' => 'something here'
		];
		return new GWCore\CobExec($sm, $config);
	},
	'Sqimp' => function($sm){
		return new GWCore\Sqimp($sm);
	},
];

$factoryBuilder = function () use ($modInvokeFactories, $modCustomFactories) {
	
	$invoke = [];
	// auto create/convert invokables to factories
	foreach ($modInvokeFactories as $k => $o) {
		$object = "\\{$o}";
		$invoke[$k] = function ($sm) use ($object) {
			return new $object($sm);
		};
	}
	foreach ($modCustomFactories as $k => $o) {
		$invoke[$k] = &$o;
	}
	
	return $invoke;
};


return [
	'service_manager' => [
		'factories' => $factoryBuilder(),
	],
];

What I did was cheat here. I could have just written out all the closure functions in the factories. But I don’t want to. I like this better. Its easier for me and my partner to add and maintain class entries by updating the $modInvokeFactories just like they were invokables before. In our case invokables are not working for too many classes and they required passing in the SM directly on the closure function.

Those classes all use ServiceManagerAwareInterface, so we had update the __construct. Here’s a snippet of one of those class constructs:


namespace GWCore;
use Zend\ServiceManager\ServiceManager;

class MyClassExample
{
	protected $sm;
	
	public function __construct(ServiceManager $sm = null)
	{
		if ($sm instanceof ServiceManager) {
			$this->sm = $sm;
			$this->init();
		}
	}
	
	
	public function init()
	{
		$this->db = $this->sm->get('FBDb');
		$this->config = $this->sm->get('config');
		$this->log = $this->sm->get('FBLogUtil');
		$this->cor = $this->sm->get('CoreFunctions');
	}
	
	// lots of cool code below...
}

So there we go. With this I could minimize my refactoring a bit. Now I just need to undo the stupid extra factory builds in my main MVC module for all the controllers and use this function instead. If is slows things down, I’ll change it, but so far I don’t see any difference. Also, I don’t even know if this will work for sure in ZF3, so… yeah. we’ll see about that soon enough. :)