Welcome to Abdul Malik Ikhsan's Blog

Zend Framework 2 : Using $creationOptions in PluginManager

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on August 14, 2014

zf2-zendframework2$creationOptions is an optional parameter that we can pass on 2nd parameter when we get service from PluginManager. It can ease coding process when we need to pass a different parameter or injection on the fly while service is created. We can apply it in $invokableClasses or $factories.

Let’s take a look more deep with example. Let’s say, we have a module with PluginManager that collect service in there like this :
invokables-and-creationoptions

We need to call ‘bar’ service with injected Foo class. Ok, let’s start.
1. create PluginInterface

namespace ExamplePluginManagerCreationOptions\Service;

interface PluginInterface
{
    /*
     * @return void
     */  
    public function getProperty();
}

2. create a Foo class that implements PluginInterface

namespace ExamplePluginManagerCreationOptions\Service;

class Foo implements PluginInterface
{
    /**
     * @var string
     */
    protected $fooProperty;
    
    /**
     * Construct with $fooProperty param
     * @param array $fooProperty
     */
    public function __construct(array $fooProperty)
    {
        $this->fooProperty = $fooProperty;    
    }
    
    /**
     * {@inheritdoc}
     */
    public function getProperty()
    {
        var_dump($this);
    }
}

3. create a Bar class that implements PluginInterface and use Foo in __construct.

namespace ExamplePluginManagerCreationOptions\Service;

class Bar implements PluginInterface
{
    /**
     * @var Foo
     */
    protected $foo;
    
    /**
     * Construct with $foo param
     * @param Foo $foo
     */
    public function __construct(Foo $foo)
    {
        $this->foo = $foo;    
    }
    
    /**
     * {@inheritdoc}
     */
    public function getProperty()
    {
        var_dump($this);
    }
}

4. When realizing the dependency above. We usually use factory to create service, but with $creationOptions, we can make it as invokables like the following :

namespace ExamplePluginManagerCreationOptions\Service;
 
use Zend\ServiceManager\AbstractPluginManager;
 
class PluginManager extends AbstractPluginManager
{
    protected $invokableClasses = array(
        'bar' => 'ExamplePluginManagerCreationOptions\Service\Bar',
    );
 
    /**
     * {@inheritdoc}
     */
    public function validatePlugin($plugin)
    {
        if ($plugin instanceof PluginInterface) {
            // we're okay
            return;
        }
 
        throw new \InvalidArgumentException(sprintf(
            'Plugin of type %s is invalid; must implement %s\PluginInterface',
            (is_object($plugin) ? get_class($plugin) : gettype($plugin)),
            __NAMESPACE__
        ));
    }
}

5. Then, we register the pluginmanager at PluginManagerFactory as PLUGIN_MANAGER_CLASS const as usual.

namespace ExamplePluginManagerCreationOptions\Factory;
 
use Zend\Mvc\Service\AbstractPluginManagerFactory;
 
class PluginManagerFactory extends AbstractPluginManagerFactory
{
    const PLUGIN_MANAGER_CLASS = 'ExamplePluginManagerCreationOptions\Service\PluginManager';
}

6. Now, we can register at service_manager key in module.config.php

return array(
    'service_manager' => array(
        'factories' => array(
            'testpluginmanager' => 'ExamplePluginManagerCreationOptions\Factory\PluginManagerFactory',  
        ),
    ),
);

7. Ok, then now we can call it :

$testpluginmanager = $this->getServiceLocator()->get('testpluginmanager');
        
$bar = $testpluginmanager->get(
    'bar',
    new \ExamplePluginManagerCreationOptions\Service\Foo(array('test'))
);
$bar->getProperty();

Hm.., do you feel pass everything of instance(s) on the fly when creating service is too much but still want “on the fly” freedom ? Then we can create a factory for it. We can then add “BarServiceFactory” like the following structure :
factories-and-creationoptions
The “BarServiceFactory” can be like the following :

namespace ExamplePluginManagerCreationOptions\Factory\Service;

use ExamplePluginManagerCreationOptions\Service\Bar;
use ExamplePluginManagerCreationOptions\Service\Foo;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\MutableCreationOptionsInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

/**
 * a factory to create ExamplePluginManagerCreationOptions\Service\Bar
 * service
 */
class BarServiceFactory implements
    FactoryInterface,
    MutableCreationOptionsInterface
{
    /**
     * {@inheritdoc}
     */
    public function setCreationOptions(array $creationOptions)
    {
        $this->creationOptions = $creationOptions;
    }
    
    /**
     * {@inheritdoc}
     */
    public function createService(ServiceLocatorInterface $serviceLocator)
    {
        //you can call other service by $serviceLocator()->get('servicename')
        //as far as the service implements PluginInterface defined in
        //your pluginmanager
        //otherwise, you need to call top serviceManager
        //by $serviceLocator->getServiceLocator()->get('servicename')
        return new Bar(new Foo($this->creationOptions));
    }
}

Now, we can register it at out PluginManager.

namespace ExamplePluginManagerCreationOptions\Service;
 
use Zend\ServiceManager\AbstractPluginManager;
 
class PluginManager extends AbstractPluginManager
{
    protected $invokableClasses = array(
        'bar' => 'ExamplePluginManagerCreationOptions\Service\Bar',
    );
    
    protected $factories = array(
        'barservice' => 'ExamplePluginManagerCreationOptions\Factory\Service\BarServiceFactory',
    );
    
    /**
     * {@inheritdoc}
     */
    public function validatePlugin($plugin)
    {
        if ($plugin instanceof PluginInterface) {
            // we're okay
            return;
        }
 
        throw new \InvalidArgumentException(sprintf(
            'Plugin of type %s is invalid; must implement %s\PluginInterface',
            (is_object($plugin) ? get_class($plugin) : gettype($plugin)),
            __NAMESPACE__
        ));
    }
}

And now, we can call it with :

$testpluginmanager = $this->getServiceLocator()->get('testpluginmanager');
        
$bar = $testpluginmanager->get(
    'barservice', 
    array('test')
);
$bar->getProperty();

When everything is ok, then we can get the following data output :

object(ExamplePluginManagerCreationOptions\Service\Bar)[303]
  protected 'foo' => 
    object(ExamplePluginManagerCreationOptions\Service\Foo)[298]
      protected 'fooProperty' => 
        array (size=1)
          0 => string 'test' (length=4)

Done ;). I hope it is useful.

Note : We can make $this->creationOptions as collection of array of instances too.

References :
1. http://zend-framework-community.634137.n4.nabble.com/Abstract-Factory-for-custom-Validators-options-discarded-td4661990.html
2. http://zend-framework-community.634137.n4.nabble.com/AbstractPluginManager-amp-options-creationOptions-will-not-work-as-newInstanceArgs-td4656077.html#a4656083