zendframework / zend-expressive

PSR-15 middleware in minutes!
BSD 3-Clause "New" or "Revised" License
711 stars 197 forks source link

Problem with zend-servicemanager ^2.7.3 and Zend ViewHelper factory #354

Closed pdrosos closed 8 years ago

pdrosos commented 8 years ago

Hi,

Not sure in which module is the problem, so will report it here.

I am using Expressive with Zend View. When switching to zend-servicemanager ^2.7.3 (2.7.6 installed) to be able to install DoctrineORMModule, I have a problem in my custom ViewHelper factory:

use Interop\Container\ContainerInterface;

class SlimFlashViewHelperFactory
{
    public function __invoke(ContainerInterface $container)
    {
    }
}

With zend-servicemanager 2.7.6 the $container parameter is of type Zend\View\HelperPluginManager and I am not able to fetch my services because of this error:

Zend\View\HelperPluginManager can only create instances of Zend\View\Helper\HelperInterface and/or callables; Slim\Flash\Messages is invalid

With zend-servicemanager 3.1.0 the $container is Zend\ServiceManager\ServiceManager and everything works as expected.

weierophinney commented 8 years ago

There are two things going on here.

First, zend-servicemanager's plugin manager implementations have some differences between version 2 and version 3, and these are highlighted in its migration guide. The main thing to be aware of is that in v2, the plugin managers pass themselves to factories, whereas in v3, the parent container is passed. What this means is that you should test to see what version you have, and pull the parent container if necessary:

public function __invoke(ContainerInterface $container)
{
    if (method_exists($container, 'getServiceLocator')) {
        $container = $container->getServiceLocator() ?: $container;
    }
    /* ... */
}

Now, the bigger issue is that you appear to be returning an invalid helper, which is indicated by the exception you list:

can only create instances of Zend\View\Helper\HelperInterface
and/or callables; Slim\Flash\Messages is invalid

If Slim\Flash\Messages is of neither type (if it does not implement HelperInterface and/or does not implement __invoke()), then it will be invalid regardless. As such, you'd need to provide an alternate implementation, or wrap it in a closure (which you could then return as the helper instance).

pdrosos commented 8 years ago

@weierophinney thanks a lot!

I have a service factory, which returns instance of Slim\Flash\Messages. The problem was I needed the parent container to be able to fetrch this service and didn't realize why I get Zend\View\HelperPluginManager instead.

Adding the code you suggested in the beginning of my view helper factory solved the issue:

use Interop\Container\ContainerInterface;
use Slim\Flash\Messages;
use App\Common\View\Helper\SlimFlashViewHelper;

class SlimFlashViewHelperFactory
{
    public function __invoke(ContainerInterface $container)
    {
        if (method_exists($container, 'getServiceLocator')) {
            $container = $container->getServiceLocator() ?: $container;
        }

        $messages = $container->get(Messages::class);
        $viewHelper = new SlimFlashViewHelper($messages);

        return $viewHelper;
    }
}
weierophinney commented 8 years ago

Major versions often mean BC breaks. :grin: Glad the suggestions I made helped you resolve the issue!