Eliminating Module Dependencies with Interfaces in ZF2

Published on September 23, 2012 by

Zend Framework 2 is even more focused on best practices than its predecessor. There are, however, built in some shortcuts to ease the programmers’ job, but if you are one of those people who want every little thing to be perfect, then you might find this article useful. We are not going to accomplish anything new in this article, but just slightly change an approach.

A module’s Module.php will look something like the below initially.

namespace MyModule;

class Module {
    public function getAutoloaderConfig() {
        return array(
            'Zend\Loader\ClassMapAutoloader' => array(
                __DIR__ . '/autoload_classmap.php',
            ),
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                ),
            ),
        );
    }

    public function getConfig() {
        return include(__DIR__ . '/config/module.config.php');
    }
}

If you are just starting out with Zend Framework 2, then you may have wondered where the methods getAutoloaderConfig and getConfig come from. How are they called and why are those method names used? To really know what it going on, we will turn out attention to the Module Manager Listeners:

Zend\ModuleManager\Listener\AutoloaderListener
This listener checks each module to see if it has implemented Zend\ModuleManager\Feature\AutoloaderProviderInterface or simply defined the getAutoloaderConfig() method. If so, it calls the getAutoloaderConfig() method on the module class and passes the returned array to Zend\Loader\AutoloaderFactory.

Let us keep the above in mind while looking at the Zend\ModuleManager\Listener\AutoloaderListener classes’ source code:

class AutoloaderListener extends AbstractListener
{
    public function __invoke(ModuleEvent $e)
    {
        $module = $e->getModule();
        if (!$module instanceof AutoloaderProviderInterface
            && !method_exists($module, 'getAutoloaderConfig')
        ) {
            return;
        }
        $autoloaderConfig = $module->getAutoloaderConfig();
        AutoloaderFactory::factory($autoloaderConfig);
    }
}

We are not going to go into details of how the module loading works, because there is already a very useful article about it in the manual. However, as you can see in the code above, it is checking whether or not the Module class implements the AutoloaderProviderInterface or simply contains a getAutoloaderConfig method. If so, this method will be called. As it turns out, we have the choice of either just using these hard coded method names (the same applies for the getConfig method) or to implement an interface. The hard coded method names serve as a convenient method for programmers to get up and running quickly, and this simplifies things in the Getting Started Guide, for instance. However, this is not all too useful if you want to know what is really going on behind the scenes.

If we look at the AutoloaderProviderInterface interface, we will see that it simply contains one method.

interface AutoloaderProviderInterface
{
    /**
     * Return an array for passing to Zend\Loader\AutoloaderFactory.
     *
     * @return array
     */
    public function getAutoloaderConfig();
}

We can then make use of this interface in our Moduleclass instead of using hard coded method names. We are going to do the same with the getConfig method. This is called from within Zend\ModuleManager\Listener\ConfigListener where it is checked whether or not the Zend\ModuleManager\Feature\ConfigProviderInterface interface is implemented or if the method just exists – exactly as we saw earlier. We will use both of the interfaces that we have discussed so that our Module class will look like this:

namespace MyModule;

use Zend\ModuleManager\Feature\ConfigProviderInterface;
use Zend\ModuleManager\Feature\AutoloaderProviderInterface;

class Module implements ConfigProviderInterface, AutoloaderProviderInterface {
    public function getAutoloaderConfig() {
        return array(
            'Zend\Loader\ClassMapAutoloader' => array(
                __DIR__ . '/autoload_classmap.php',
            ),
            'Zend\Loader\StandardAutoloader' => array(
                'namespaces' => array(
                    __NAMESPACE__ => __DIR__ . '/src/' . __NAMESPACE__,
                ),
            ),
        );
    }

    public function getConfig() {
        return include(__DIR__ . '/config/module.config.php');
    }
}

So what is the difference? This is a very valid question, and the answer may seem ridiculous and utterly trivial to some. Exactly the same thing will be accomplished – execution will even go by the same code path when loading our module. The difference is simply whether or not you want to use hard coded method names. This is a dependency and tendency that is often accepted within the PHP community for whatever reason; probably that it is often convenient. However, using strongly typed and compiled languages such as Java or C#, you would rarely want to do this. In fact, it is almost a sin.

It is probably more of a theoretical discussion, but what happens if these hard coded method names change? Then your application will break because your methods no longer match the hard coded strings, and you might be left wondering what is going on. If you have used the interfaces and those interfaces change, then your IDE should let you know that something is not right. Therefore, you will automatically be notified before you try to update your application and not at runtime. For those who like to have an IDE as a “safety net,” this will help accomplish that. Of course the Zend Framework Team is fully aware that they cannot change these strings and therefore it will not happen (they should also have unit tests ensuring this), hence why it is more of a “religious” matter. However, applications are generally more robust and less error prone when avoiding these dependencies.

The concept of interfaces is beautiful, but more so when they are actually used.

Featured

Learn Zend Framework today!

Take an online course and become a ZF2 ninja!

Here is what you will learn:

  • Understand the theory of Zend Framework in details
  • How to implement an enterprise-ready architecture
  • Develop professional applications in Zend Framework
  • Proficiently work with databases in Zend Framework
  • ... and much more!
Zend Framework logo
Author avatar
Bo Andersen

About the Author

I am a back-end web developer with a passion for open source technologies. I have been a PHP developer for many years, and also have experience with Java and Spring Framework. I currently work full time as a lead developer. Apart from that, I also spend time on making online courses, so be sure to check those out!

2 comments on »Eliminating Module Dependencies with Interfaces in ZF2«

  1. Alaa Qashou

    Totally agree with you, I do JAVA android and iOS programing using Objective C , and I really use Interfaces a lot for dependency .

    but in ZendFramework , I had to read about Module Manager and Listener to know about hardcoded Methods to really understand whats going. and I find it hard to track whats happening when I try understand how ZfcUser and other modules work. but I believe over time it will be easier and I really like zf2 but still had a lot to read to master. Thank you very much for your website and please keep writing tutorials its really helping ZF2 developers.

    • Hello Alaa,

      Thank you very much for your comment and kind words.

      Indeed, the interfaces make the code easier to understand, read and navigate. I believe the only reason developers are able to not use them is an attempt to keep the framework simple to get up to speed with. That being said, hard coded logic like this can make it more difficult to understand how the framework works internally. In the end I believe it is a fair choice, because one can freely choose which approach to use. It just takes a little bit more effort to truly understand how the Module Manager operates.

      I am happy that you liked this post, and I look forward to seeing you back on the site in the future. :-)

Leave a Reply

Your e-mail address will not be published.