2

In my ZF2 application, I use Zend\Di\Di to create all my class instances. The DI definitions are scanned using Zend\Di\Definition\CompilerDefinition and cached using APC. This avoids scanning the classes at runtime using slow reflection. If there is an exception during instance creation (because of outdated DI definitions), the code is rescanned, the definitions are cached, and the instance is created again.

This is very convenient during development, since no factory methods/closures have to be written or changed for new classes or changed constructors. My constructors follow a certain convention (only type-hinted arguments and a singe $params array) to ensure that dependency injection works without specifying additional constructor params.

Up till now, this performs nicely, avoids bugs (no outdated factory methods), and speeds up development. However, the scanned definitions are currently 1.8MB (serialized array) in APC, and growing. Since they have to be loaded from cache upon every request, I fear that memory will be exhausted if there are too many requests in a short time. I have no load testing setup to simulate this, though.

I'm aware that the recommended way is to use Zend\ServiceManager and write factory closures for every class, instead of using Zend\Di\Di. But I imagine this is a lot of work and quite annoying during development.

Do you recommend refactoring to Zend\ServiceManager in this situation?

aimfeld
  • 2,931
  • 7
  • 32
  • 43
  • 1
    If you have the ZF2 project locally running then you could try a Apache benchmark with a high concurrency to see if memory problems occur. I would also recommand to rewrite to use the servicemanager because I guess the DI component will be deprecated in the future. – Kees Schepers Mar 01 '13 at 10:00
  • 2
    @KeesSchepers the Di component won't be deprecated: I'll fight myself through a bloodshed before that is EVER gonna happen. – Ocramius Mar 01 '13 at 16:29

2 Answers2

2

I have written a ZF2 module to help you solve this problem: https://github.com/aimfeld/ZendDiCompiler

ZendDiCompiler is a Zend Framework 2 module that uses auto-generated factory code for dependency-injection. It saves you a lot of work, since there's no need anymore for writing Zend\ServiceManager factory closures and keeping them up-to-date manually.

ZendDiCompiler scans your code (using Zend\Di) and creates factory methods automatically. If the factory methods are outdated, ZendDiCompiler updates them in the background. Therefore, you develop faster, avoid bugs due to outdated factory methods, and experience great performance in production!

aimfeld
  • 2,931
  • 7
  • 32
  • 43
1

This is exactly what I exposed in my blogpost about Zend\Di and RAD.

While Zend\Di is good for development, it is a huge memory and performance bottleneck in production. Even if you cache definitions, it still uses ReflectionClass and call_user_func_array() to manipulate your instances. It also has to perform a lot of surrounding operations that are necessary to align injection parameters: just take a look at the number of calls to array_merge during instantiation of an object via Zend\Di.

I wrote a module to handle compiling of Zend\Di\Di instance managers to closures: OcraDiCompiler. It needs a bit of cleanup, but its job is to generate code factory/closures by tracing instantiation logic of Zend\Di internals. If you want to "revive" it a bit, I'd be glad to help out on that, since it's in my TODO list, but with low priority right now.

Anyway, moving instantiation logic to factories/closures through the ServiceManager is not as much work as you'd expect, so don't be scared and give it a try: you may like it.

Ocramius
  • 25,171
  • 7
  • 103
  • 107
  • Wow, awesome, such a compiler is exactly what I need! I'll definitely try it! I have about 500 classes and I don't want to manually maintain 500 factory closures for them. The constructors are very clean and I hope I can compile the closures with your compiler :). – aimfeld Mar 01 '13 at 16:21
  • Btw, Ocramius, that's the project I was telling you about that was recently migrated from ZF1 to ZF2 and only Zend_Db left. – markus Mar 01 '13 at 16:30
  • @Ocramius, I tried the OcraDiCompiler but I didn't really get it to work since I have a somewhat custom DI setup. However, I extended Zend\Di\ServiceLocator\Generator so it can handle shared instances properly. It generates almost 7000 lines of factory code and it automatically regenerates the service locator in case of outdated code. I indeed noticed a considerable performance gain, too. – aimfeld Mar 02 '13 at 21:53
  • @Wunibald that's awesome :) If you can share your setup as an issue on OcraDiCompiler it would even be more awesome anyway ;) – Ocramius Mar 02 '13 at 22:39
  • 1
    @Ocramius Sure, I'm happy to share. After some cleaning up, I'll attach you my code as an issue on OcraDiCompiler. – aimfeld Mar 06 '13 at 14:15
  • 1
    @Ocramius I made my solution more generic and I decided to provide it as a ZF2 module: https://github.com/aimfeld/di-wrapper. Suggestions are very welcome! – aimfeld Mar 07 '13 at 22:34
  • @aimfeld will look into it shortly! :) – Ocramius Mar 08 '13 at 08:33