Skip to content
This repository has been archived by the owner on Jan 8, 2020. It is now read-only.

Adding logic to check module dependencies at module loading time #3443

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -0,0 +1,22 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_ModuleManager
*/

namespace Zend\ModuleManager\Exception;

/**
* Runtime Exception
*
* @category Zend
* @package Zend_ModuleManager
* @subpackage Exception
*/
class MissingDependencyModuleException extends RuntimeException implements ExceptionInterface
{
}
@@ -0,0 +1,26 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_ModuleManager
*/

namespace Zend\ModuleManager\Feature;

/**
* @category Zend
* @package Zend_ModuleManager
* @subpackage Feature
*/
interface DependencyIndicatorInterface
{
/**
* Expected to return an array of modules on which the current one depends on
*
* @return array
*/
public function getDependencyModules();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhabs rename the method to getDependendModules() ? It would be more clear, what you get.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, that would be the modules that depend on this one.

dependency is correct

}
Expand Up @@ -54,6 +54,11 @@ public function attach(EventManagerInterface $events)
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE_RESOLVE, new ModuleResolverListener);
// High priority, because most other loadModule listeners will assume the module's classes are available via autoloading
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new AutoloaderListener($options), 9000);

if ($options->getCheckDependencies()) {
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new ModuleDependencyCheckerListener, 8000);
}

$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new InitTrigger($options));
$this->listeners[] = $events->attach(ModuleEvent::EVENT_LOAD_MODULE, new OnBootstrapListener($options));
$this->listeners[] = $events->attach($locatorRegistrationListener);
Expand Down
29 changes: 29 additions & 0 deletions library/Zend/ModuleManager/Listener/ListenerOptions.php
Expand Up @@ -57,6 +57,11 @@ class ListenerOptions extends AbstractOptions
*/
protected $cacheDir;

/**
* @var bool
*/
protected $checkDependencies = true;

/**
* Get an array of paths where modules reside
*
Expand Down Expand Up @@ -265,6 +270,30 @@ public function setCacheDir($cacheDir)
return $this;
}

/**
* Set whether to check dependencies during module loading or not
*
* @return string
*/
public function getCheckDependencies()
{
return $this->checkDependencies;
}

/**
* Set whether to check dependencies during module loading or not
*
* @param bool $checkDependencies the value to be set
*
* @return ListenerOptions
*/
public function setCheckDependencies($checkDependencies)
{
$this->checkDependencies = (bool) $checkDependencies;

return $this;
}

/**
* Normalize a path for insertion in the stack
*
Expand Down
@@ -0,0 +1,58 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_ModuleManager
*/

namespace Zend\ModuleManager\Listener;

use Zend\ModuleManager\Exception;
use Zend\ModuleManager\Feature\DependencyIndicatorInterface;
use Zend\ModuleManager\ModuleEvent;

/**
* Module resolver listener
*
* @category Zend
* @package Zend_ModuleManager
* @subpackage Listener
*/
class ModuleDependencyCheckerListener
{
/**
* @var array of already loaded modules, indexed by module name
*/
protected $loaded = array();

/**
* @param \Zend\ModuleManager\ModuleEvent $e
*
* @throws Exception\MissingDependencyModuleException
*/
public function __invoke(ModuleEvent $e)
{
$module = $e->getModule();

if ($module instanceof DependencyIndicatorInterface || method_exists($module, 'getDependencyModules')) {
$dependencies = $module->getDependencyModules();

foreach ($dependencies as $dependencyModule) {
if (!isset($this->loaded[$dependencyModule])) {
throw new Exception\MissingDependencyModuleException(
sprintf(
'Module "%s" depends on module "%s", which was not initialized before it',
$e->getModuleName(),
$dependencyModule
)
);
}
}
}

$this->loaded[$e->getModuleName()] = true;
}
}
Expand Up @@ -79,6 +79,7 @@ public function testDefaultListenerAggregateCanAttachItself()
),
'loadModule' => array(
'Zend\ModuleManager\Listener\AutoloaderListener',
'Zend\ModuleManager\Listener\ModuleDependencyCheckerListener',
'Zend\ModuleManager\Listener\InitTrigger',
'Zend\ModuleManager\Listener\OnBootstrapListener',
'Zend\ModuleManager\Listener\ConfigListener',
Expand Down
@@ -0,0 +1,67 @@
<?php
/**
* Zend Framework (http://framework.zend.com/)
*
* @link http://github.com/zendframework/zf2 for the canonical source repository
* @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
* @package Zend_ModuleManager
*/

namespace ZendTest\ModuleManager\Listener;

use PHPUnit_Framework_TestCase as TestCase;
use Zend\ModuleManager\Listener\ModuleDependencyCheckerListener;
use Zend\ModuleManager\ModuleManager;

class ModuleDependencyCheckerListenerTest extends TestCase
{
/**
* @covers \Zend\ModuleManager\Listener\ModuleDependencyCheckerListener::__invoke
*/
public function testCallsGetDependencyModulesOnModuleImplementingInterface()
{
//$moduleManager = new ModuleManager(array());
//$moduleManager->getEventManager()->attach('loadModule', new ModuleDependencyCheckerListener(), 2000);

$module = $this->getMock('Zend\ModuleManager\Feature\DependencyIndicatorInterface');
$module->expects($this->once())->method('getDependencyModules')->will($this->returnValue(array()));

$event = $this->getMock('Zend\ModuleManager\ModuleEvent');
$event->expects($this->any())->method('getModule')->will($this->returnValue($module));

$listener = new ModuleDependencyCheckerListener();
$listener->__invoke($event);
}

/**
* @covers \Zend\ModuleManager\Listener\ModuleDependencyCheckerListener::__invoke
*/
public function testCallsGetDependencyModulesOnModuleNotImplementingInterface()
{
$module = $this->getMock('stdClass', array('getDependencyModules'));
$module->expects($this->once())->method('getDependencyModules')->will($this->returnValue(array()));

$event = $this->getMock('Zend\ModuleManager\ModuleEvent');
$event->expects($this->any())->method('getModule')->will($this->returnValue($module));

$listener = new ModuleDependencyCheckerListener();
$listener->__invoke($event);
}

/**
* @covers \Zend\ModuleManager\Listener\ModuleDependencyCheckerListener::__invoke
*/
public function testNotFulfilledDependencyThrowsException()
{
$module = $this->getMock('stdClass', array('getDependencyModules'));
$module->expects($this->once())->method('getDependencyModules')->will($this->returnValue(array('OtherModule')));

$event = $this->getMock('Zend\ModuleManager\ModuleEvent');
$event->expects($this->any())->method('getModule')->will($this->returnValue($module));

$listener = new ModuleDependencyCheckerListener();
$this->setExpectedException('Zend\ModuleManager\Exception\MissingDependencyModuleException');
$listener->__invoke($event);
}
}