Auto-wiring for Zend ServiceManager

Writing factories for zend-servicemanager can be a tedious, repetitive task. Most of factories I write follow the same pattern: pull some dependencies from the container, instantiate new object and return it. How can you avoid the repetition?

Use case

Say we have a service for processing e-mails. It consumes two dependencies: mail transport, and renderer:

class Mailer
{
    public function __construct(MailTransportInterface $transport, MailRenderer $renderer)
    {
      // ...
    }
}

Your factory will likely look like this:

class MailerFactory
{
    public function __invoke(ContainerInterface $container)
    {
        return new Mailer(
            $container->get(MailTransportInterface::class),
            $container->get(MailRenderer::class)
        );
    }
}

As you see, it is very simple: factory pulls services typehinted in constructor, and creates new instance. In my projects, usually 80%-90% of all factories are using this pattern. Clearly it is something that should be automated.

Existing solutions

To avoid re-inventing the wheel, always try to find existing solution to the problem you're facing. I looked into two:

  • zend-di - can be bridged with zend-servicemanager. However it is overcomplicated for my needs and does not support caching, making it slow.
  • php-di - looks really nice, but doesn't integrate well with zend-servicemanager - in order to use it, I would need to rewrite tons of existing code.

There are also other container implementations (like Aura.Di or Symfony DependencyInjection), that support auto-wiring, unfortunately all of them share the same problem as php-di - I cannot easily use them in my current projects.

So, no luck. I had to roll-up my sleeves and wrote my own implementation.

Blast\ReflectionFactory

Inspired by a new feature of zend-mvc, I created a general factory capable of auto-wiring PHP classes. I made a few assumptions that match my coding style:

  • no setter-injections: all required dependencies are injected in constructor
  • every service is identified in dependency container using its FQCN

I also didnt want to invent swiss army knife that does everything - for example, my solution does not work for services created with scalar configuration values (SMTP transport is good example here - you need to feed it with settings like hostname and credentials). Still, even with this limitation I can cover most of my needs. If service requires some logic to be created, it is perfectly fine to write a factory.

Usage

My library is available on Packagist - you can install it using composer:

$ composer require mtymek/blast-reflection-factory

After installation, you can start using it within your dependency configuration:

use Blast\ReflectionFactory\ReflectionFactory;

return [
    'dependencies' => [
        'factories' => [
            // use normal factory for classes that require complex instantiation 
            SmtpMailTransport::class => SmtpMailTransportFactory::class,
             
            // use ReflectionFactory for auto-wiring
            MailRenderer::class => ReflectionFactory::class,
            Mailer::class => ReflectionFactory::class,
        ],
        'aliases' => [
            MailTransportInterface::class => SmtpMailTransport::class,
        ],
    ]
];

You don't need change any of the existing code - once ReflectionFactory is installed, you can use it for new services that you write.

Caching

Auto-wiring uses PHP Reflection to read typehints of service constructor. It is slow process compared to instantiating objects directly, so ReflectionFactory allows caching the result. You can use it by pointing location of cache file, using enableCache method:

\Blast\ReflectionFactory\ReflectionFactory::enableCache(
    'data/cache/reflection-factory.cache.php'
);

If you are using Zend Expressive Skeleton Application, put above code in config/container.php file.

Conclusion

Who likes coding factories?

I'm pretty sure this little package will increase productivity of my team. Feel free to use it and give me feedback on GitHub!

Read more 

Extreme caching with PSR-7

PSR-7 brought some interesting patterns that can be applied to PHP application regardless of what framework it uses. It is particularly interesting when it comes to performance - no matter what technology your project uses, you can apply the same techniques to make it faster.

Here I will show how PSR-7 middleware can be used to cache application's output. I call it "extreme caching", because I want to trigger it as early as possible, in order to reduce amount of code to be executed on each request.

I will present this pattern on Zend Expressive-based application. It will work for any PSR-7 framework that uses middleware with following signature (which has become de facto standard):

function ($request, $response, $next) { }
Read more 

Speeding up PHP application bootstrap with Class Dumper

One reasons for PHP still being considered slow is a consequence of how it works under web server environment: every time a client sends request, application is initialized from scratch - it runs all the bootstrap code. Bootstrapping is repeated over and over again, for every connecting client.

While this is an obvious waste of resources, it is also very difficult to avoid without rewriting an application under different architecture. Is there anything that could be done to at least reduce impact of application bootstrap, without making any changes to actual application? As it turns out, there is.

Read more 

PSR-7: HTTP Messages Today

PSR-7 is here and is big. Now, more than one month after it was voted, a lot of work has been put into projects supporting this standard. Even though we’re still at the beginning of this great journey, it is exciting to see what is already available, thanks to great work of PHP community.

Watching related projects since the inception of that standard, I will present packages that can serve as foundation for actual applications: HTTP message implementations, dispatchers and micro frameworks.

Read more 

ZF-Console: PHP microframework for console applications

As you may know, folks from ZF2/Apigility projects keep bringing interesting utility modules, enriching ZF2 ecosystem. Today I discovered small framework/helper for writing console applications: ZF-Console. And, to my surprise, it was based on my own pull request from last year! I was really proud to see that I brought something useful to the community.

Read more 

Testing ZF2 module services

There's an important question often rising when working on Zend Framework 2 module: should I test service factories? After all, they are usually trivial, they create some object and inject it with dependencies from ServiceManager. Having one test per factory seems to be an overkill.

Better to go one step back, and ask yourself a question: what exactly do you want to test?

Read more 

About me
Mateusz Tymek

After compiling my very first lines of code at the age of 12, I became passionate about computer science and technology. Now I'm a PHP developer, enjoying my work as a member of Cleeng team.
Doing some sports in my spare time.

Github activity