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

Commit

Permalink
Merge branch 'feature/40' into develop
Browse files Browse the repository at this point in the history
Close #44
Close #40
  • Loading branch information
weierophinney committed Nov 6, 2017
2 parents d9ae75c + 92825b8 commit b59d7d6
Show file tree
Hide file tree
Showing 3 changed files with 269 additions and 99 deletions.
26 changes: 6 additions & 20 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

All notable changes to this project will be documented in this file, in reverse chronological order by release.

## 1.1.0 - TBD
## 1.1.0 - 2017-11-06

### Added

Expand All @@ -20,25 +20,11 @@ All notable changes to this project will be documented in this file, in reverse

### Fixed

- Nothing.

## 1.0.1 - TBD

### Added

- Nothing.

### Deprecated

- Nothing.

### Removed

- Nothing.

### Fixed

- Nothing.
- [#40](https://github.com/zendframework/zend-component-installer/pull/40) and
[#44](https://github.com/zendframework/zend-component-installer/pull/44) fix
an issue whereby packages that define an array of paths for a PSR-0 or PSR-4
autoloader would cause the installer to error. The installer now properly
handles these situations.

## 1.0.0 - 2017-04-25

Expand Down
222 changes: 144 additions & 78 deletions src/ComponentInstaller.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

namespace Zend\ComponentInstaller;

use ArrayObject;
use Composer\Composer;
use Composer\EventDispatcher\EventSubscriberInterface;
use Composer\Installer\PackageEvent;
Expand Down Expand Up @@ -203,7 +204,7 @@ public function onPostPackageInstall(PackageEvent $event)

/**
* Find all Module classes in the package and their dependencies
* - method `getModuleDependencies` of Module class.
* via method `getModuleDependencies` of Module class.
*
* These dependencies are used later
* @see \Zend\ComponentInstaller\Injector\AbstractInjector::injectAfterDependencies
Expand All @@ -216,87 +217,13 @@ public function onPostPackageInstall(PackageEvent $event)
*/
private function loadModuleClassesDependencies(PackageInterface $package)
{
$dependencies = [];
$dependencies = new ArrayObject([]);
$installer = $this->composer->getInstallationManager();
$packagePath = $installer->getInstallPath($package);

$autoload = $package->getAutoload();
foreach ($autoload as $type => $map) {
foreach ($map as $namespace => $path) {
switch ($type) {
case 'classmap':
$fullPath = sprintf('%s/%s', $packagePath, $path);
if (is_dir(rtrim($fullPath, '/'))) {
$modulePath = sprintf('%s%s', $fullPath, 'Module.php');
} elseif (substr($path, -10) === 'Module.php') {
$modulePath = $fullPath;
} else {
continue 2;
}
break;
case 'files':
if (substr($path, -10) !== 'Module.php') {
continue 2;
}
$modulePath = sprintf('%s/%s', $packagePath, $path);
break;
case 'psr-0':
$modulePath = sprintf(
'%s/%s%s%s',
$packagePath,
$path,
str_replace('\\', '/', $namespace),
'Module.php'
);
break;
case 'psr-4':
$modulePath = sprintf(
'%s/%s%s',
$packagePath,
$path,
'Module.php'
);
break;
default:
continue 2;
}

if (file_exists($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];
}
}
}
$this->mapAutoloaders($package->getAutoload(), $dependencies, $packagePath);

return [];
return $dependencies->getArrayCopy();
}

/**
Expand Down Expand Up @@ -687,4 +614,143 @@ private function cacheInjector(Injector\InjectorInterface $injector, $packageTyp
{
$this->cachedInjectors[$packageType] = $injector;
}

/**
* Iterate through each autoloader type to find dependencies.
*
* @param array $autoload List of autoloader types and associated autoloader definitions.
* @param ArrayObject $dependencies Module dependencies defined by the module.
* @param string $packagePath Path to the package on the filesystem.
* @return void
*/
private function mapAutoloaders(array $autoload, ArrayObject $dependencies, $packagePath)
{
foreach ($autoload as $type => $map) {
$this->mapType($map, $type, $dependencies, $packagePath);
}
}

/**
* Iterate through a single autolaoder type to find dependencies.
*
* @param array $map Map of namespace => path(s) pairs.
* @param string $type Type of autoloader being iterated.
* @param ArrayObject $dependencies Module dependencies defined by the module.
* @param string $packagePath Path to the package on the filesystem.
* @return void
*/
private function mapType(array $map, $type, ArrayObject $dependencies, $packagePath)
{
foreach ($map as $namespace => $paths) {
$paths = (array) $paths;
$this->mapNamespacePaths($paths, $namespace, $type, $dependencies, $packagePath);
}
}

/**
* Iterate through the paths defined for a given namespace.
*
* @param array $paths Paths defined for the given namespace.
* @param string $namespace PHP namespace to which the paths map.
* @param string $type Type of autoloader being iterated.
* @param ArrayObject $dependencies Module dependencies defined by the module.
* @param string $packagePath Path to the package on the filesystem.
* @return void
*/
private function mapNamespacePaths(array $paths, $namespace, $type, ArrayObject $dependencies, $packagePath)
{
foreach ($paths as $path) {
$this->mapPath($path, $namespace, $type, $dependencies, $packagePath);
}
}

/**
* Find module dependencies for a given namespace for a given path.
*
* @param string $path Path to inspect.
* @param string $namespace PHP namespace to which the paths map.
* @param string $type Type of autoloader being iterated.
* @param ArrayObject $dependencies Module dependencies defined by the module.
* @param string $packagePath Path to the package on the filesystem.
* @return void
*/
private function mapPath($path, $namespace, $type, ArrayObject $dependencies, $packagePath)
{
switch ($type) {
case 'classmap':
$fullPath = sprintf('%s/%s', $packagePath, $path);
if (substr($path, -10) === 'Module.php') {
$modulePath = $fullPath;
break;
}

$modulePath = sprintf('%s/Module.php', rtrim($fullPath, '/'));
break;
case 'files':
if (substr($path, -10) !== 'Module.php') {
return;
}
$modulePath = sprintf('%s/%s', $packagePath, $path);
break;
case 'psr-0':
$modulePath = sprintf(
'%s/%s%s%s',
$packagePath,
$path,
str_replace('\\', '/', $namespace),
'Module.php'
);
break;
case 'psr-4':
$modulePath = sprintf(
'%s/%s%s',
$packagePath,
$path,
'Module.php'
);
break;
default:
return;
}

if (! file_exists($modulePath)) {
return;
}

$result = $this->getModuleDependencies($modulePath);

if (empty($result)) {
return;
}

// Mimic array + array operation in ArrayObject
$dependencies->exchangeArray($dependencies->getArrayCopy() + $result);
}

/**
* @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 [];
}
}
Loading

0 comments on commit b59d7d6

Please sign in to comment.