0

Could you please give a hint why PHP-DI integration with Zend Framework 2 is not working for me (reproduced with Apache/2.4.9 (Win64) PHP/5.5.12 and Apache/2.2.22 (Win32) PHP/5.3.13).

composer.json:

{
    "require": {
        "php": ">=5.3.3",
        "zendframework/zendframework": "2.3.5",
        "mnapoli/php-di": "4.4.6",
        "mnapoli/php-di-zf2": "0.3.0",
       ...
},
    ...

config\application.config.php:

<?php
return array(
    'modules' => array(
        'Morpho',
        'DI\ZendFramework2',
    ),
    'module_listener_options' => array(
        'module_paths' => array(
            './module',
            './vendor',
        ),
    ),
);
?>

module/Morpho/config.module.config.php:

<?php
return array(
    'service_manager' => array(
        'factories' => array(
            'DI\Container' => function() {
                $builder = new DI\ContainerBuilder();
                $builder->addDefinitionsFromFile("config/di.yml");
                return $builder->build();
            },
        ),
    ),
    'router' => array(
        ...
    ),
    'controllers' => array(
        ...
    ),
    'view_manager' => array(
        ...
    ),
);

config/di.yml:

Morpho\Service\PartOfSpeechService:
    class: Morpho\Service\PhpMorphyPartOfSpeechService

module/Morpho/src/Morpho/Controller/PartOfSpeechController:

class PartOfSpeechController extends AbstractRestfulController {
    ...
    /**
     * @Inject
     * @var PartOfSpeechService
     */
    public $partOfSpeechService;

    public function processPostData(Request $request) {
        $partsOfSpeech = $this->partOfSpeechService->getPartsOfSpeech("test", "en_EN");
        return new JsonModel($partsOfSpeech);
    }
}

When running this code under apache each time I get:

PHP Fatal error:  Uncaught exception 'Zend\ModuleManager\Exception\RuntimeException'
with message 'Module (DI\ZendFramework2) could not be initialized.' in \vendor\zendframework\zendframework\library\Zend\ModuleManager\ModuleManager.php:195
Stack trace:
0 \vendor\zendframework\zendframework\library\Zend\ModuleManager\ModuleManager.php(169): Zend\ModuleManager\ModuleManager->loadModuleByName(Object(Zend\ModuleManager\ModuleEvent))
1 \vendor\zendframework\zendframework\library\Zend\ModuleManager\ModuleManager.php(96): Zend\ModuleManager\ModuleManager->loadModule('DI\ZendFramewor...')
2 [internal function]: Zend\ModuleManager\ModuleManager->onLoadModules(Object(Zend\ModuleManager\ModuleEvent))
3 \vendor\zendframework\zendframework\library\Zend\EventManager\EventManager.php(468):
call_user_func(Array, Object(Zend\ModuleManager\ModuleEvent))
4 \vendor\zendframework\zendframework\library\Zend\EventManager\EventManager.php(207): Zend\EventM in \vendor\zendframework\zendframework\library\Zend\ModuleManager\ModuleManager.php on line 195

Any your thoughts would be really appreciated.

Maksim
  • 1,231
  • 1
  • 15
  • 25
  • Why don't you just use zf2 Service Manager? http://framework.zend.com/manual/current/en/modules/zend.service-manager.quick-start.html – Purple Hexagon Feb 23 '15 at 13:43
  • That's a very good question. Honestly saying I'm a very new to ZendFramework. I've discovered SM just recently. For me the DI approach used by PHP-DI (with annotations) looked much more close to Spring @Inject. That's why I stuck with it. I'll try to rewrite with SM and come back with the results here. – Maksim Feb 23 '15 at 14:40

3 Answers3

1

It doesn't work because you are using the old YAML syntax, but since PHP-DI v4.0 the syntax is now PHP.

Head over to the documentation to learn about the syntax: http://php-di.org/doc/definition.html

Matthieu Napoli
  • 48,448
  • 45
  • 173
  • 261
  • Ah that makes sense now, tried to bugfix the issue in a vanilla install of zf2 with the provided op's config but couldn't quite work out the error. – Purple Hexagon Feb 26 '15 at 13:15
  • I have to admit this is a weird edge case :/ Basically now that it is PHP config, the config file must return a PHP array. Here it's a YAML file, so when it's `include`d it will return null. PHP-DI then consider `null` as empty config, but it could show an error though that's a very weird situation IMO. – Matthieu Napoli Feb 26 '15 at 23:52
0

I followed the suggestion given by Purple Hexagon and here is a working implementation using Service Manager:

module/Morpho/config:

...
'service_manager' => array(
    'services' => array(
        "PartOfSpeechService" => new Morpho\Service\PhpMorphyPartOfSpeechService(),
    ),
),
...

module/Morpho/src/Morpho/Controller/PartOfSpeechController.php:

class PartOfSpeechController extends AbstractRestfulController {
    ...
    public function processPostData(Request $request) {
        $serviceManager = $this->getServiceLocator();
        $partsOfSpeech = $serviceManager->get("PartOfSpeechService")->getPartsOfSpeech($request->getPost("phrase"),
                $request->getPost("language"));
        return new JsonModel($partsOfSpeech);
    }
}

Why I don't like this:

  1. I have to use a "dummy" code to obtain serviceManager. That's "dummy" because that's not related to business logic of my application at all.
  2. The Dependency Injection approach provided by ServiceManager makes my code dependent on the ServiceManager itself. Typically I should not care how a bean/object is injected and so shouldn't refer to any kind of container or ServiceManager in my code.

I think PHP-DI is much more closer to the bean injection model used by Java Spring (that I believe is good). Unfortunately it is still not working for me. And finally, the approach of obtaining object from container was working in PHP-DI as well.

Maksim
  • 1,231
  • 1
  • 15
  • 25
0

For a service:

factory config:

        'factories' => array(
            'MyService' => 'Application\Factory\MyService',
        ),

Factory class:

class MyService implements FactoryInterface
{

    public function createService(ServiceLocatorInterface $serviceManager)
    {
        $purifier = new MyService($serviceManager->get('MyAwesomeDependency'));
        return $purifier;
    }
}

For a controller:

ControllerFactory.php:

class PartOfSpeechControllerFactory
{
    public function __invoke($serviceLocator)
    {
        // Service locator here is the ControllerManager so get ServiceManager 
        $serviceManager = $serviceLocator->getServiceLocator();

        $controller = new PartOfSpeechController($serviceManager->get('PartOfSpeechService'));

        return $controller;
    }
}

class PartOfSpeechController.php

class PartOfSpeechController extends AbstractRestfulController {


    protected $partOfSpeechService;

    public function __construct(PartOfSpeechService $partOfSpeechService)
    {
         $this->partOfSpeechService = $partOfSpeechService;
    }


    public function processPostData(Request $request) {
         $var = $this->partOfSpeechService->serviceMethod();   
    }
}

The config for controller:

'factories' => array(
                'Application\Controller\PartOfSpeechController' => 'Application\Factory\PartOfSpeechControllerFactory'
        ),
Purple Hexagon
  • 3,538
  • 2
  • 24
  • 45
  • A question, the usage of the service is still imply: $serviceManager = $this->getServiceLocator(); $serviceManager->get("MyService")->doSmth(); – Maksim Feb 23 '15 at 16:01
  • The example is for a service but the config is basically the same for a controller – Purple Hexagon Feb 23 '15 at 16:01
  • I have added another example, does that answer your question. The controller has no dependency on the locator only the factory – Purple Hexagon Feb 23 '15 at 16:13
  • On a side note the code and functionality looks very similar to Zend\Di ... http://framework.zend.com/manual/current/en/modules/zend.di.introduction.html ... if you prefer container to locator perhaps you could try using that? – Purple Hexagon Feb 23 '15 at 16:59