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

Commit

Permalink
Merge 5453816 into f6d41a4
Browse files Browse the repository at this point in the history
  • Loading branch information
michalbundyra committed Oct 14, 2016
2 parents f6d41a4 + 5453816 commit 1419005
Show file tree
Hide file tree
Showing 12 changed files with 197 additions and 40 deletions.
1 change: 1 addition & 0 deletions phpcs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@
<file>src</file>
<file>test</file>
<exclude-pattern>test/Injector/TestAsset/*.php</exclude-pattern>
<exclude-pattern>test/TestAsset/Module*.php</exclude-pattern>
</ruleset>
55 changes: 46 additions & 9 deletions src/ComponentInstaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ public function onPostPackageInstall(PackageEvent $event)
return;
}

$this->includeModuleClasses($package);
$dependencies = $this->loadModuleClassesDependencies($package);
$applicationModules = $this->findApplicationModules();

$this->marshalInstallableModules($extra, $options)
Expand All @@ -190,26 +190,32 @@ public function onPostPackageInstall(PackageEvent $event)
return $injectors;
}, new Collection([]))
// Inject modules into configuration
->each(function ($injector, $module) use ($name, $packageTypes, $applicationModules) {
->each(function ($injector, $module) use ($name, $packageTypes, $applicationModules, $dependencies) {
if (isset($dependencies[$module])) {
$injector->setModuleDependencies($dependencies[$module]);
}

$injector->setApplicationModules($applicationModules);
$this->injectModuleIntoConfig($name, $module, $injector, $packageTypes[$module]);
});
}

/**
* Find all Module classes in the package and include them.
* Module classes is used later
* Find all Module classes in the package and their dependencies
* - method `getModuleDependencies` of Module class.
*
* These dependencies are used later
* @see \Zend\ComponentInstaller\Injector\AbstractInjector::injectAfterDependencies
* - to get package dependencies - module method `getModuleDependencies`
* and add component in a correct order on the module list.
* to add component in a correct order on the module list - after dependencies.
*
* It works with PSR-0, PSR-4, 'classmap' and 'files' composer autoloading.
*
* @param PackageInterface $package
* @return void
* @return array
*/
private function includeModuleClasses(PackageInterface $package)
private function loadModuleClassesDependencies(PackageInterface $package)
{
$dependencies = [];
$installer = $this->composer->getInstallationManager();
$packagePath = $installer->getInstallPath($package);

Expand Down Expand Up @@ -255,10 +261,41 @@ private function includeModuleClasses(PackageInterface $package)
}

if (file_exists($modulePath)) {
include $modulePath;
if ($result = $this->getModuleDependencies($modulePath)) {
$dependencies += $result;
}
}
}
}

return $dependencies;
}

/**
* @param string $file
* @return array
*/
private function getModuleDependencies($file)
{
$content = file_get_contents($file);
if (preg_match('/namespace\s+([^\s]+)\s*;/', $content, $m)) {
$moduleName = $m[1];

// @codingStandardsIgnoreStart
$regExp = '/public\s+function\s+getModuleDependencies\s*\(\s*\)\s*{[^}]*return\s*(?:array\(|\[)([^})\]]*)(\)|\])/';
// @codingStandardsIgnoreEnd
if (preg_match($regExp, $content, $m)) {
$dependencies = array_filter(
explode(',', stripslashes(rtrim(preg_replace('/[\s"\']/', '', $m[1]), ',')))
);

if ($dependencies) {
return [$moduleName => $dependencies];
}
}
}

return [];
}

/**
Expand Down
70 changes: 39 additions & 31 deletions src/Injector/AbstractInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,13 @@ abstract class AbstractInjector implements InjectorInterface
*/
protected $applicationModules = [];

/**
* Dependencies of the module.
*
* @var array
*/
protected $moduleDependencies = [];

/**
* Constructor
*
Expand Down Expand Up @@ -161,6 +168,7 @@ public function inject($package, $type, IOInterface $io)
}

if ($type == self::TYPE_COMPONENT
&& $this->moduleDependencies
&& $this->injectAfterDependencies($package, $config, $io)
) {
return;
Expand Down Expand Up @@ -195,42 +203,32 @@ public function inject($package, $type, IOInterface $io)
*/
private function injectAfterDependencies($package, $config, IOInterface $io)
{
$moduleClass = sprintf('%s\\%s', $package, 'Module');
if (class_exists($moduleClass) && method_exists($moduleClass, 'getModuleDependencies')) {
$module = new $moduleClass();
$dependencies = $module->getModuleDependencies();

if ($dependencies) {
foreach ($dependencies as $dependency) {
if (! $this->isRegisteredInConfig($dependency, $config)) {
$io->write(sprintf(
'<error> Dependency %s is not registered in the configuration</error>',
$dependency
));

return true;
}
}

$lastDependency = $this->findLastDependency($dependencies, $config);

$pattern = sprintf(
$this->injectionPatterns[self::TYPE_DEPENDENCY]['pattern'],
preg_quote($lastDependency, '/')
);
$replacement = sprintf(
$this->injectionPatterns[self::TYPE_DEPENDENCY]['replacement'],
$package
);

$config = preg_replace($pattern, $replacement, $config);
file_put_contents($this->configFile, $config);
foreach ($this->moduleDependencies as $dependency) {
if (! $this->isRegisteredInConfig($dependency, $config)) {
$io->write(sprintf(
'<error> Dependency %s is not registered in the configuration</error>',
$dependency
));

return true;
}
}

return false;
$lastDependency = $this->findLastDependency($this->moduleDependencies, $config);

$pattern = sprintf(
$this->injectionPatterns[self::TYPE_DEPENDENCY]['pattern'],
preg_quote($lastDependency, '/')
);
$replacement = sprintf(
$this->injectionPatterns[self::TYPE_DEPENDENCY]['replacement'],
$package
);

$config = preg_replace($pattern, $replacement, $config);
file_put_contents($this->configFile, $config);

return true;
}

/**
Expand Down Expand Up @@ -331,6 +329,16 @@ public function setApplicationModules(array $modules)
return $this;
}

/**
* {@inheritDoc}
*/
public function setModuleDependencies(array $modules)
{
$this->moduleDependencies = $modules;

return $this;
}

/**
* Remove a package from the configuration.
*
Expand Down
8 changes: 8 additions & 0 deletions src/Injector/ConfigInjectorChain.php
Original file line number Diff line number Diff line change
Expand Up @@ -137,4 +137,12 @@ public function setApplicationModules(array $modules)
{
return $this;
}

/**
* {@inheritDoc}
*/
public function setModuleDependencies(array $modules)
{
return $this;
}
}
8 changes: 8 additions & 0 deletions src/Injector/InjectorInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,4 +65,12 @@ public function remove($package, IOInterface $io);
* @return self
*/
public function setApplicationModules(array $modules);

/**
* Set dependencies for the module.
*
* @param array $modules
* @return self
*/
public function setModuleDependencies(array $modules);
}
8 changes: 8 additions & 0 deletions src/Injector/NoopInjector.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,12 @@ public function setApplicationModules(array $modules)
{
return $this;
}

/**
* {@inheritDoc}
*/
public function setModuleDependencies(array $modules)
{
return $this;
}
}
28 changes: 28 additions & 0 deletions test/ComponentInstallerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1445,4 +1445,32 @@ public function testPrependComponentAndAppendModule()
'Some\Module',
], $modules);
}

public function moduleClass()
{
return [
[__DIR__ . '/TestAsset/ModuleBadlyFormatted.php', ['BadlyFormatted\Application' => ['Dependency1']]],
[__DIR__ . '/TestAsset/ModuleWithDependencies.php', ['MyNamespace' => ['Dependency']]],
[__DIR__ . '/TestAsset/ModuleWithInterface.php', ['LongArray\Application' => ['Foo\D1', 'Bar\D2']]],
[__DIR__ . '/TestAsset/ModuleWithoutDependencies.php', []],
[__DIR__ . '/TestAsset/ModuleWithEmptyArrayDependencies.php', []],
];
}

/**
* @dataProvider moduleClass
*
* @param string $file
* @param array $result
*/
public function testGetModuleDependenciesFromModuleClass($file, $result)
{
$r = new \ReflectionObject($this->installer);
$rm = $r->getMethod('getModuleDependencies');
$rm->setAccessible(true);

$dependencies = $rm->invoke($this->installer, $file);

$this->assertEquals($result, $dependencies);
}
}
14 changes: 14 additions & 0 deletions test/TestAsset/ModuleBadlyFormatted.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php
namespace
BadlyFormatted\Application

;

class Module{
public function
getModuleDependencies(){
return

[ "Dependency1" ];
}
}
10 changes: 10 additions & 0 deletions test/TestAsset/ModuleWithDependencies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace MyNamespace;

class Module
{
public function getModuleDependencies()
{
return ['Dependency'];
}
}
10 changes: 10 additions & 0 deletions test/TestAsset/ModuleWithEmptyArrayDependencies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace Foo\NewApplication;

class Module
{
public function getModuleDependencies()
{
return [];
}
}
15 changes: 15 additions & 0 deletions test/TestAsset/ModuleWithInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php
namespace LongArray\Application;

use ClassMap\SomeInterface;

class Module implements SomeInterface
{
public function getModuleDependencies()
{
return array(
'Foo\\D1',
'Bar\\D2',
);
}
}
10 changes: 10 additions & 0 deletions test/TestAsset/ModuleWithoutDependencies.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php
namespace MyNamespace;

class Module
{
public function getConfig()
{
return [];
}
}

0 comments on commit 1419005

Please sign in to comment.