diff --git a/Dockerfile b/Dockerfile
index 1d8651f9..06ec0fdb 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,14 +1,20 @@
FROM composer:2.7.7
FROM php:8.3.9-cli-alpine AS base
-FROM base as builder
+FROM base AS builder
RUN apk update && apk add git
+RUN apk add --update linux-headers
+RUN apk add --no-cache $PHPIZE_DEPS \
+ && pecl install xdebug-3.3.2 \
+ && docker-php-ext-enable xdebug
+COPY ./docker/php/xdebug.ini /usr/local/etc/php/conf.d/docker-php-ext-xdebug.ini
+COPY ./docker/php/error_reporting.ini /usr/local/etc/php/conf.d/error_reporting.ini
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY ./ /mozart/
WORKDIR /mozart/
RUN composer install
-FROM builder as packager
+FROM builder AS packager
RUN rm -rf vendor
RUN composer install --no-dev -o
diff --git a/composer.json b/composer.json
index eda74610..05f06aa5 100644
--- a/composer.json
+++ b/composer.json
@@ -12,7 +12,8 @@
"prefer-stable": true,
"license": "MIT",
"require": {
- "php": "^8.0"
+ "php": "^8.0",
+ "netresearch/jsonmapper": "^4.4"
},
"autoload": {
"psr-4": {
diff --git a/docker-compose.yml b/docker-compose.yml
index 8835bc8f..a8d243c1 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,3 @@
-version: '3.4'
services:
builder:
build:
diff --git a/docker/php/error_reporting.ini b/docker/php/error_reporting.ini
new file mode 100644
index 00000000..7e566f46
--- /dev/null
+++ b/docker/php/error_reporting.ini
@@ -0,0 +1 @@
+error_reporting=E_ALL
diff --git a/docker/php/xdebug.ini b/docker/php/xdebug.ini
new file mode 100644
index 00000000..9c37b9c1
--- /dev/null
+++ b/docker/php/xdebug.ini
@@ -0,0 +1,6 @@
+zend_extension=xdebug
+
+[xdebug]
+xdebug.mode=develop,debug
+xdebug.client_host=host.docker.internal
+xdebug.start_with_request=yes
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
index 27d5af45..a83ef6c4 100644
--- a/phpcs.xml.dist
+++ b/phpcs.xml.dist
@@ -5,7 +5,7 @@
-
+
./src
diff --git a/src/Composer/Autoload/Autoloader.php b/src/Composer/Autoload/Autoloader.php
index 9aa6b218..15f190a9 100644
--- a/src/Composer/Autoload/Autoloader.php
+++ b/src/Composer/Autoload/Autoloader.php
@@ -4,6 +4,9 @@
interface Autoloader
{
- public function processConfig($autoloadConfig);
- public function getSearchNamespace();
+ /**
+ * @param mixed $autoloadConfig
+ */
+ public function processConfig($autoloadConfig): void;
+ public function getSearchNamespace(): string;
}
diff --git a/src/Composer/Autoload/Classmap.php b/src/Composer/Autoload/Classmap.php
deleted file mode 100644
index 981df843..00000000
--- a/src/Composer/Autoload/Classmap.php
+++ /dev/null
@@ -1,36 +0,0 @@
-files, $value);
- } else {
- array_push($this->paths, $value);
- }
- }
- }
-
- /**
- * @throws \Exception
- *
- * @return void
- */
- public function getSearchNamespace()
- {
- throw new \Exception('Classmap autoloaders do not contain a namespace and this method can not be used.');
- }
-}
diff --git a/src/Composer/Autoload/NamespaceAutoloader.php b/src/Composer/Autoload/NamespaceAutoloader.php
index f8393995..62ef6328 100644
--- a/src/Composer/Autoload/NamespaceAutoloader.php
+++ b/src/Composer/Autoload/NamespaceAutoloader.php
@@ -12,7 +12,7 @@ abstract class NamespaceAutoloader implements Autoloader
*
* e.g. src/
*
- * @var string[]
+ * @var array
*/
public $paths = [];
@@ -20,29 +20,29 @@ abstract class NamespaceAutoloader implements Autoloader
* A package's composer.json config autoload key's value, where $key is `psr-1`|`psr-4`|`classmap`.
*
* @param $autoloadConfig
- *
- * @return void
*/
- public function processConfig($autoloadConfig)
+ public function processConfig($autoloadConfig): void
{
- foreach ($autoloadConfig as $key => $value) {
- $this->namespace = $key;
- array_push($this->paths, $value);
+ if (is_array($autoloadConfig)) {
+ foreach ($autoloadConfig as $path) {
+ array_push($this->paths, $path);
+ }
+ } else {
+ array_push($this->paths, $autoloadConfig);
}
}
- /**
- * @return string
- */
- public function getSearchNamespace()
+ public function getNamespace(): string
{
- return $this->namespace;
+ return rtrim($this->namespace, '\\') . '\\';
}
- /**
- * @return string
- */
- public function getNamespacePath()
+ public function getSearchNamespace(): string
+ {
+ return rtrim($this->namespace, '\\');
+ }
+
+ public function getNamespacePath(): string
{
return '';
}
diff --git a/src/Composer/Autoload/Psr0.php b/src/Composer/Autoload/Psr0.php
deleted file mode 100644
index 2a009821..00000000
--- a/src/Composer/Autoload/Psr0.php
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace, '\\');
- }
-
- /**
- * @return string
- */
- public function getNamespacePath()
- {
- return str_replace('\\', DIRECTORY_SEPARATOR, $this->namespace);
- }
-}
diff --git a/src/Composer/Package.php b/src/Composer/Package.php
deleted file mode 100644
index 78800fe8..00000000
--- a/src/Composer/Package.php
+++ /dev/null
@@ -1,69 +0,0 @@
-path = $path;
- $this->config = json_decode(file_get_contents($this->path . '/composer.json'));
-
- if (isset($overrideAutoload)) {
- $this->config->autoload = $overrideAutoload;
- }
- }
-
- /**
- * @return void
- */
- public function findAutoloaders()
- {
- $namespace_autoloaders = array(
- 'psr-0' => 'CoenJacobs\Mozart\Composer\Autoload\Psr0',
- 'psr-4' => 'CoenJacobs\Mozart\Composer\Autoload\Psr4',
- 'classmap' => 'CoenJacobs\Mozart\Composer\Autoload\Classmap',
- );
-
- if (! isset($this->config->autoload)) {
- return;
- }
-
- foreach ($namespace_autoloaders as $key => $value) {
- if (! isset($this->config->autoload->$key)) {
- continue;
- }
-
- $autoloadConfig = (array)$this->config->autoload->$key;
-
- /** @var Autoloader $autoloader */
- $autoloader = new $value();
- $autoloader->processConfig($autoloadConfig);
-
- array_push($this->autoloaders, $autoloader);
- }
- }
-}
diff --git a/src/Config/Autoload.php b/src/Config/Autoload.php
new file mode 100644
index 00000000..55fd1aae
--- /dev/null
+++ b/src/Config/Autoload.php
@@ -0,0 +1,67 @@
+ */
+ public array $autoloaders = [];
+
+ public function setupAutoloaders(stdClass $autoloadData): void
+ {
+ $autoloaders = [];
+
+ if (isset($autoloadData->{'psr-4'})) {
+ $psr4Autoloaders = (array) $autoloadData->{'psr-4'};
+ foreach ($psr4Autoloaders as $key => $value) {
+ $autoloader = new Psr4();
+ $autoloader->namespace = $key;
+ $autoloader->processConfig($value);
+ $autoloaders[] = $autoloader;
+ }
+ }
+
+ if (isset($autoloadData->{'psr-0'})) {
+ $psr0Autoloaders = (array) $autoloadData->{'psr-0'};
+ foreach ($psr0Autoloaders as $key => $value) {
+ $autoloader = new Psr0();
+ $autoloader->namespace = $key;
+ $autoloader->processConfig($value);
+ $autoloaders[] = $autoloader;
+ }
+ }
+
+ if (isset($autoloadData->classmap)) {
+ $autoloader = new Classmap();
+ $autoloader->processConfig($autoloadData->classmap);
+ $autoloaders[] = $autoloader;
+ }
+
+ $this->setAutoloaders($autoloaders);
+ }
+
+ /**
+ * @param array $autoloaders
+ */
+ public function setAutoloaders(array $autoloaders): void
+ {
+ foreach ($autoloaders as $autoloader) {
+ if (! $autoloader instanceof Autoloader) {
+ continue;
+ }
+
+ array_push($this->autoloaders, $autoloader);
+ }
+ }
+
+ /**
+ * @return Autoloader[]
+ */
+ public function getAutoloaders(): array
+ {
+ return $this->autoloaders;
+ }
+}
diff --git a/src/Config/Classmap.php b/src/Config/Classmap.php
new file mode 100644
index 00000000..c53529ed
--- /dev/null
+++ b/src/Config/Classmap.php
@@ -0,0 +1,37 @@
+files, $value);
+ } else {
+ array_push($this->paths, $value);
+ }
+ }
+ }
+
+ /**
+ * @throws Exception
+ */
+ public function getSearchNamespace(): string
+ {
+ throw new Exception('Classmap autoloaders do not contain a namespace and this method can not be used.');
+ }
+}
diff --git a/src/Config/Extra.php b/src/Config/Extra.php
new file mode 100644
index 00000000..327482b3
--- /dev/null
+++ b/src/Config/Extra.php
@@ -0,0 +1,13 @@
+mozart;
+ }
+}
diff --git a/src/Config/Mozart.php b/src/Config/Mozart.php
new file mode 100644
index 00000000..a07c5b5b
--- /dev/null
+++ b/src/Config/Mozart.php
@@ -0,0 +1,134 @@
+packages;
+ }
+
+ /**
+ * @param string[] $packages
+ */
+ public function setPackages(array $packages): void
+ {
+ $this->packages = $packages;
+ }
+
+ /**
+ * @param string[] $excluded_packages
+ */
+ public function setExcludedPackages(array $excluded_packages): void
+ {
+ $this->excluded_packages = $excluded_packages;
+ }
+
+ public function setOverrideAutoload(stdClass $object): void
+ {
+ $this->override_autoload = new OverrideAutoload($object);
+ }
+
+ public function isValidMozartConfig(): bool
+ {
+ $required = [ 'dep_namespace', 'dep_directory', 'classmap_directory', 'classmap_prefix' ];
+
+ foreach ($required as $requiredProp) {
+ if (empty($this->$requiredProp)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ public function isExcludedPackage(Package $package): bool
+ {
+ return in_array($package->getName(), $this->getExcludedPackages());
+ }
+
+ /**
+ * Returns the configured dependency directory, with an appended directory
+ * separator, if one isn't at the end of the configured string yet.
+ */
+ public function getDepDirectory(): string
+ {
+ return rtrim($this->dep_directory, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
+ }
+
+ public function getClassmapDirectory(): string
+ {
+ return rtrim($this->classmap_directory, DIRECTORY_SEPARATOR).DIRECTORY_SEPARATOR;
+ }
+
+ public function getDeleteVendorDirectories(): bool
+ {
+ return $this->delete_vendor_directories;
+ }
+
+ public function getDependencyNamespace(): string
+ {
+ $namespace = preg_replace("/\\\{2,}$/", "\\", $this->dep_namespace."\\");
+
+ if (empty($namespace)) {
+ throw new Exception('Could not get target dependency namespace');
+ }
+
+ return $namespace;
+ }
+
+ public function getClassmapPrefix(): string
+ {
+ return $this->classmap_prefix;
+ }
+
+ public function getOverrideAutoload(): OverrideAutoload
+ {
+ return $this->override_autoload;
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getExcludedPackages(): array
+ {
+ return $this->excluded_packages;
+ }
+
+ public function setWorkingDir(string $workingDir): void
+ {
+ $this->workingDir = $workingDir;
+ }
+
+ public function getWorkingDir(): string
+ {
+ return $this->workingDir;
+ }
+}
diff --git a/src/Config/OverrideAutoload.php b/src/Config/OverrideAutoload.php
new file mode 100644
index 00000000..42967cb5
--- /dev/null
+++ b/src/Config/OverrideAutoload.php
@@ -0,0 +1,34 @@
+
+ */
+class OverrideAutoload extends ArrayObject
+{
+ public function __construct(stdClass $objects)
+ {
+ $objects = (array) $objects;
+
+ $storage = [];
+
+ foreach ($objects as $key => $object) {
+ $storage[$key] = $object;
+ }
+
+ parent::__construct($storage);
+ }
+
+ public function getByKey(string $key): mixed
+ {
+ if (! isset($this[$key])) {
+ return null;
+ }
+
+ return $this[$key];
+ }
+}
diff --git a/src/Config/Package.php b/src/Config/Package.php
new file mode 100644
index 00000000..c466ee9f
--- /dev/null
+++ b/src/Config/Package.php
@@ -0,0 +1,117 @@
+setupAutoloaders($data);
+ $this->autoload = $autoload;
+ }
+
+ public function getExtra(): ?Extra
+ {
+ return $this->extra;
+ }
+
+ public function isValidMozartConfig(): bool
+ {
+ if (empty($this->getExtra())) {
+ return false;
+ }
+
+ if (empty($this->getExtra()->getMozart())) {
+ return false;
+ }
+
+ return $this->getExtra()->getMozart()->isValidMozartConfig();
+ }
+
+ public function getName(): string
+ {
+ return $this->name;
+ }
+
+ /**
+ * @return Autoloader[]
+ */
+ public function getAutoloaders(): array
+ {
+ if (empty($this->autoload)) {
+ return array();
+ }
+
+ return $this->autoload->getAutoloaders();
+ }
+
+ /**
+ * @return string[]
+ */
+ public function getRequire(): array
+ {
+ return array_keys($this->require);
+ }
+
+ /**
+ * @return Package[]
+ */
+ public function getDependencies(): array
+ {
+ return $this->dependencies;
+ }
+
+ public function loadDependencies(): void
+ {
+ $finder = PackageFinder::instance();
+ if ($this->isValidMozartConfig() && !empty($this->getExtra())) {
+ $mozart = $this->getExtra()->getMozart();
+
+ if (empty($mozart)) {
+ throw new Exception("Couldn't load dependencies because config not set.");
+ }
+ $finder->setConfig($mozart);
+ }
+
+ $dependencies = $finder->getPackagesBySlugs($this->getRequire());
+
+ $this->registerDependencies($dependencies);
+ }
+
+ public function registerDependency(Package $package): void
+ {
+ array_push($this->dependencies, $package);
+ }
+
+ /**
+ * @param Package[] $packages
+ */
+ public function registerDependencies(array $packages): void
+ {
+ foreach ($packages as $package) {
+ $this->registerDependency($package);
+ }
+ }
+}
diff --git a/src/Config/Psr0.php b/src/Config/Psr0.php
new file mode 100644
index 00000000..6f664d22
--- /dev/null
+++ b/src/Config/Psr0.php
@@ -0,0 +1,9 @@
+namespace, '\\');
+ }
+
+ public function getNamespacePath(): string
+ {
+ return str_replace('\\', DIRECTORY_SEPARATOR, $this->namespace);
+ }
+}
diff --git a/src/Config/ReadsConfig.php b/src/Config/ReadsConfig.php
new file mode 100644
index 00000000..4e7f3d30
--- /dev/null
+++ b/src/Config/ReadsConfig.php
@@ -0,0 +1,69 @@
+ $config
+ */
+ public static function loadFromArray(array $config): self
+ {
+ $encoded = json_encode($config);
+
+ if (! $encoded) {
+ throw new Exception('Could not read config from provided array.');
+ }
+
+ $config = json_decode($encoded, false);
+
+ if (! $config) {
+ throw new Exception('Could not read config from provided array.');
+ }
+
+ return self::loadFromStdClass($config);
+ }
+
+ public static function loadFromStdClass(stdClass $config): self
+ {
+ $mapper = new JsonMapper();
+ $mapper->bEnforceMapType = false;
+ $object = $mapper->map($config, self::class);
+
+ if (! $object instanceof self) {
+ throw new Exception('Could not read config from provided array.');
+ }
+
+ return $object;
+ }
+
+ public static function loadFromString(string $config): self
+ {
+ $config = json_decode($config);
+
+ $mapper = new JsonMapper();
+ $mapper->bEnforceMapType = false;
+ $object = $mapper->map($config, self::class);
+
+ if (! $object instanceof self) {
+ throw new Exception('Could not read config from provided array.');
+ }
+
+ return $object;
+ }
+}
diff --git a/src/Console/Commands/Compose.php b/src/Console/Commands/Compose.php
index dd9a567e..aad21e6a 100644
--- a/src/Console/Commands/Compose.php
+++ b/src/Console/Commands/Compose.php
@@ -2,31 +2,31 @@
namespace CoenJacobs\Mozart\Console\Commands;
-use CoenJacobs\Mozart\Composer\Package;
+use CoenJacobs\Mozart\Config\Mozart;
use CoenJacobs\Mozart\Mover;
+use CoenJacobs\Mozart\PackageFactory;
+use CoenJacobs\Mozart\PackageFinder;
use CoenJacobs\Mozart\Replacer;
+use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
class Compose extends Command
{
- /** @var Mover */
- private $mover;
+ private Mover $mover;
+ private Replacer $replacer;
+ private Mozart $config;
+ private PackageFinder $finder;
+ private string $workingDir;
- /** @var Replacer */
- private $replacer;
-
- /** @var string */
- private $workingDir;
-
- /** @var */
- private $config;
+ public function __construct()
+ {
+ $this->workingDir = getcwd();
+ parent::__construct();
+ }
- /**
- * @return void
- */
- protected function configure()
+ protected function configure(): void
{
$this->setName('compose');
$this->setDescription('Composes all dependencies as a package inside a WordPress plugin.');
@@ -35,205 +35,55 @@ protected function configure()
protected function execute(InputInterface $input, OutputInterface $output): int
{
- $workingDir = getcwd();
- $this->workingDir = $workingDir;
+ if (! $this->workingDir) {
+ throw new Exception('Could not determine working directory.');
+ }
- $composerFile = $workingDir . DIRECTORY_SEPARATOR. 'composer.json';
- if (!file_exists($composerFile)) {
- $output->write('No composer.json found at current directory: ' . $workingDir);
+ $composerFile = $this->workingDir . DIRECTORY_SEPARATOR. 'composer.json';
+ try {
+ $package = PackageFactory::createPackage($composerFile, null, false);
+ } catch (Exception $e) {
+ $output->write('Unable to read the composer.json file');
return 1;
}
- $composer = json_decode(file_get_contents($composerFile));
- // If the json was malformed.
- if (!is_object($composer)) {
- $output->write('Unable to parse composer.json read at: ' . $workingDir);
+ if (! $package->isValidMozartConfig() || empty($package->getExtra())) {
+ $output->write('Mozart config not readable in composer.json at extra->mozart');
return 1;
}
- // if `extra` is missing or not an object or if it does not have a `mozart` key which is an object.
- if (!isset($composer->extra) || !is_object($composer->extra)
- || !isset($composer->extra->mozart) || !is_object($composer->extra->mozart)) {
+ $config = $package->getExtra()->getMozart();
+
+ if (empty($config)) {
$output->write('Mozart config not readable in composer.json at extra->mozart');
return 1;
}
- $config = $composer->extra->mozart;
-
- $config->dep_namespace = preg_replace("/\\\{2,}$/", "\\", "$config->dep_namespace\\");
$this->config = $config;
+ $this->config->setWorkingDir($this->workingDir);
- $require = array();
- if (isset($config->packages) && is_array($config->packages)) {
- $require = $config->packages;
- } elseif (isset($composer->require) && is_object($composer->require)) {
- $require = array_keys(get_object_vars($composer->require));
+ $require = $this->config->getPackages();
+ if (empty($require)) {
+ $require = $package->getRequire();
}
- $packagesByName = $this->findPackages($require);
- $excludedPackagesNames = isset($config->excluded_packages) ? $config->excluded_packages : [];
- $packagesToMoveByName = array_diff_key($packagesByName, array_flip($excludedPackagesNames));
- $packages = array_values($packagesToMoveByName);
+ $this->finder = PackageFinder::instance();
+ $this->finder->setConfig($this->config);
- foreach ($packages as $package) {
- $package->dependencies = array_diff_key($package->dependencies, array_flip($excludedPackagesNames));
- }
+ $package->loadDependencies();
- $this->mover = new Mover($workingDir, $config);
- $this->replacer = new Replacer($workingDir, $config);
+ $packages = $this->finder->getPackagesBySlugs($require);
+ $packages = $this->finder->findPackages($packages);
+
+ $this->mover = new Mover($this->workingDir, $this->config);
+ $this->replacer = new Replacer($this->workingDir, $this->config);
$this->mover->deleteTargetDirs($packages);
- $this->movePackages($packages);
- $this->replacePackages($packages);
- $this->replaceParentInTree($packages);
- $this->replacer->replaceParentClassesInDirectory($this->config->classmap_directory);
+ $this->mover->movePackages($packages);
+ $this->replacer->replacePackages($packages);
+ $this->replacer->replaceParentInTree($packages);
+ $this->replacer->replaceParentClassesInDirectory($this->config->getClassmapDirectory());
return 0;
}
-
- /**
- * @param $workingDir
- * @param $config
- * @param array $packages
- *
- * @return void
- */
- protected function movePackages($packages): void
- {
- foreach ($packages as $package) {
- $this->movePackage($package);
- }
-
- $this->mover->deleteEmptyDirs();
- }
-
- /**
- * @param $workingDir
- * @param $config
- * @param array $packages
- *
- * @return void
- */
- protected function replacePackages($packages): void
- {
- foreach ($packages as $package) {
- $this->replacePackage($package);
- }
- }
-
- /**
- * Move all the packages over, one by one, starting on the deepest level of dependencies.
- *
- * @return void
- */
- public function movePackage($package): void
- {
- if (! empty($package->dependencies)) {
- foreach ($package->dependencies as $dependency) {
- $this->movePackage($dependency);
- }
- }
-
- $this->mover->movePackage($package);
- }
-
- /**
- * Replace contents of all the packages, one by one, starting on the deepest level of dependencies.
- *
- * @return void
- */
- public function replacePackage($package): void
- {
- if (! empty($package->dependencies)) {
- foreach ($package->dependencies as $dependency) {
- $this->replacePackage($dependency);
- }
- }
-
- $this->replacer->replacePackage($package);
- }
-
- /**
- * Loops through all dependencies and their dependencies and so on...
- * will eventually return a list of all packages required by the full tree.
- *
- * @param ((int|string)|mixed)[] $slugs
- *
- * @return Package[]
- */
- private function findPackages(array $slugs): array
- {
- $packages = [];
-
- foreach ($slugs as $package_slug) {
- $packageDir = $this->workingDir . DIRECTORY_SEPARATOR . 'vendor'
- . DIRECTORY_SEPARATOR . $package_slug . DIRECTORY_SEPARATOR;
-
- if (! is_dir($packageDir)) {
- continue;
- }
-
- $autoloaders = null;
- if (isset($this->config->override_autoload) && isset($this->config->override_autoload->$package_slug)) {
- $autoloaders = $this->config->override_autoload->$package_slug;
- }
-
- $package = new Package($packageDir, $autoloaders);
- $package->findAutoloaders();
-
- $config = json_decode(file_get_contents($packageDir . 'composer.json'));
-
- $dependencies = [];
- if (isset($config->require)) {
- $dependencies = array_keys((array)$config->require);
- }
-
- $package->dependencies = $this->findPackages($dependencies);
- $packages[$package_slug] = $package;
- }
-
- return $packages;
- }
-
- /**
- * Get an array containing all the dependencies and dependencies
- * @param Package $package
- * @param array $dependencies
- * @return array
- */
- private function getAllDependenciesOfPackage(Package $package, $dependencies = []): array
- {
- if (empty($package->dependencies)) {
- return $dependencies;
- }
-
- /** @var Package $dependency */
- foreach ($package->dependencies as $dependency) {
- $dependencies[] = $dependency;
- }
-
- foreach ($package->dependencies as $dependency) {
- $dependencies = $this->getAllDependenciesOfPackage($dependency, $dependencies);
- }
-
- return $dependencies;
- }
-
- /**
- * @param array $packages
- */
- private function replaceParentInTree(array $packages): void
- {
- /** @var Package $package */
- foreach ($packages as $package) {
- $dependencies = $this->getAllDependenciesOfPackage($package);
-
- /** @var Package $dependency */
- foreach ($dependencies as $dependency) {
- $this->replacer->replaceParentPackage($dependency, $package);
- }
-
- $this->replaceParentInTree($package->dependencies);
- }
- }
}
diff --git a/src/Mover.php b/src/Mover.php
index 42a78a45..4f3d65d6 100644
--- a/src/Mover.php
+++ b/src/Mover.php
@@ -3,11 +3,12 @@
namespace CoenJacobs\Mozart;
use CoenJacobs\Mozart\Composer\Autoload\Autoloader;
-use CoenJacobs\Mozart\Composer\Autoload\Classmap;
use CoenJacobs\Mozart\Composer\Autoload\NamespaceAutoloader;
-use CoenJacobs\Mozart\Composer\Autoload\Psr0;
-use CoenJacobs\Mozart\Composer\Autoload\Psr4;
-use CoenJacobs\Mozart\Composer\Package;
+use CoenJacobs\Mozart\Config\Classmap;
+use CoenJacobs\Mozart\Config\Mozart;
+use CoenJacobs\Mozart\Config\Package;
+use CoenJacobs\Mozart\Config\Psr0;
+use CoenJacobs\Mozart\Config\Psr4;
use League\Flysystem\Local\LocalFilesystemAdapter;
use League\Flysystem\Filesystem;
use Symfony\Component\Finder\Finder;
@@ -21,20 +22,20 @@ class Mover
/** @var string */
protected $targetDir;
- /** @var \stdClass */
+ /** @var Mozart */
protected $config;
/** @var Filesystem */
protected $filesystem;
- /** @var array */
+ /** @var array */
protected $movedPackages = [];
- public function __construct($workingDir, $config)
+ public function __construct(string $workingDir, Mozart $config)
{
- $this->workingDir = $workingDir;
- $this->targetDir = $config->dep_directory;
$this->config = $config;
+ $this->workingDir = $workingDir;
+ $this->targetDir = $this->config->getDepDirectory();
$adapter = new LocalFilesystemAdapter(
$this->workingDir
@@ -48,14 +49,11 @@ public function __construct($workingDir, $config)
* Create the required `dep_directory` and `classmap_directory` and delete targetDirs of packages about to be moved.
*
* @param Package[] $packages The packages that, in the next step, will be moved.
- *
- * @return void
*/
public function deleteTargetDirs($packages): void
{
- $this->filesystem->createDirectory($this->config->dep_directory);
-
- $this->filesystem->createDirectory($this->config->classmap_directory);
+ $this->filesystem->createDirectory($this->config->getDepDirectory());
+ $this->filesystem->createDirectory($this->config->getClassmapDirectory());
foreach ($packages as $package) {
$this->deleteDepTargetDirs($package);
@@ -66,63 +64,73 @@ public function deleteTargetDirs($packages): void
* Delete the directories about to be used for packages earmarked for Mozart namespacing.
*
* @visibility private to allow recursion through packages and subpackages.
- *
- * @param Package $package
- *
- * @return void
*/
- private function deleteDepTargetDirs($package): void
+ private function deleteDepTargetDirs(Package $package): void
{
- foreach ($package->autoloaders as $packageAutoloader) {
+ foreach ($package->getAutoloaders() as $packageAutoloader) {
$autoloaderType = get_class($packageAutoloader);
switch ($autoloaderType) {
case Psr0::class:
case Psr4::class:
- $outputDir = $this->config->dep_directory . $packageAutoloader->namespace;
+ $outputDir = $this->config->getDepDirectory() . $packageAutoloader->namespace;
$outputDir = str_replace('\\', DIRECTORY_SEPARATOR, $outputDir);
$this->filesystem->deleteDirectory($outputDir);
break;
case Classmap::class:
- $outputDir = $this->config->classmap_directory . $package->config->name;
+ $outputDir = $this->config->getClassmapDirectory() . $package->getName();
$outputDir = str_replace('\\', DIRECTORY_SEPARATOR, $outputDir);
$this->filesystem->deleteDirectory($outputDir);
break;
}
}
- foreach ($package->dependencies as $subPackage) {
+ foreach ($package->getDependencies() as $subPackage) {
$this->deleteDepTargetDirs($subPackage);
}
}
public function deleteEmptyDirs(): void
{
- if (count($this->filesystem->listContents($this->config->dep_directory, true)->toArray()) === 0) {
- $this->filesystem->deleteDirectory($this->config->dep_directory);
+ if (count($this->filesystem->listContents($this->config->getDepDirectory(), true)->toArray()) === 0) {
+ $this->filesystem->deleteDirectory($this->config->getDepDirectory());
}
- if (count($this->filesystem->listContents($this->config->classmap_directory, true)->toArray()) === 0) {
- $this->filesystem->deleteDirectory($this->config->classmap_directory);
+ if (count($this->filesystem->listContents($this->config->getClassmapDirectory(), true)->toArray()) === 0) {
+ $this->filesystem->deleteDirectory($this->config->getClassmapDirectory());
}
}
/**
- * @return void
+ * @param Package[] $packages
*/
- public function movePackage(Package $package)
+ public function movePackages($packages): void
+ {
+ foreach ($packages as $package) {
+ $this->movePackages($package->getDependencies());
+ $this->movePackage($package);
+ }
+
+ $this->deleteEmptyDirs();
+ }
+
+ public function movePackage(Package $package): void
{
- if (in_array($package->config->name, $this->movedPackages)) {
+ if (in_array($package->getName(), $this->movedPackages)) {
return;
}
- foreach ($package->autoloaders as $autoloader) {
+ if ($this->config->isExcludedPackage($package)) {
+ return;
+ }
+
+ foreach ($package->getAutoloaders() as $autoloader) {
if ($autoloader instanceof NamespaceAutoloader) {
$finder = new Finder();
foreach ($autoloader->paths as $path) {
$source_path = $this->workingDir . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR
- . $package->config->name . DIRECTORY_SEPARATOR . $path;
+ . $package->getName() . DIRECTORY_SEPARATOR . $path;
$source_path = str_replace('/', DIRECTORY_SEPARATOR, $source_path);
@@ -139,7 +147,7 @@ public function movePackage(Package $package)
foreach ($autoloader->files as $file) {
$source_path = $this->workingDir . DIRECTORY_SEPARATOR . 'vendor'
- . DIRECTORY_SEPARATOR . $package->config->name;
+ . DIRECTORY_SEPARATOR . $package->getName();
$finder->files()->name($file)->in($source_path);
foreach ($finder as $foundFile) {
@@ -152,7 +160,7 @@ public function movePackage(Package $package)
foreach ($autoloader->paths as $path) {
$source_path = $this->workingDir . DIRECTORY_SEPARATOR . 'vendor'
- . DIRECTORY_SEPARATOR . $package->config->name . DIRECTORY_SEPARATOR . $path;
+ . DIRECTORY_SEPARATOR . $package->getName() . DIRECTORY_SEPARATOR . $path;
$finder->files()->in($source_path);
@@ -167,40 +175,33 @@ public function movePackage(Package $package)
}
}
- if (!in_array($package->config->name, $this->movedPackages)) {
- $this->movedPackages[] = $package->config->name;
+ if (!in_array($package->getName(), $this->movedPackages)) {
+ $this->movedPackages[] = $package->getName();
}
}
- if (!isset($this->config->delete_vendor_directories) || $this->config->delete_vendor_directories === true) {
+ if ($this->config->getDeleteVendorDirectories()) {
$this->deletePackageVendorDirectories();
}
}
- /**
- * @param Package $package
- * @param Autoloader $autoloader
- * @param SplFileInfo $file
- * @param string $path
- * @return string
- */
- public function moveFile(Package $package, $autoloader, $file, $path = '')
+ public function moveFile(Package $package, Autoloader $autoloader, SplFileInfo $file, string $path = ''): string
{
if ($autoloader instanceof NamespaceAutoloader) {
$namespacePath = $autoloader->getNamespacePath();
- $replaceWith = $this->config->dep_directory . $namespacePath;
+ $replaceWith = $this->config->getDepDirectory() . $namespacePath;
$targetFile = str_replace($this->workingDir, $replaceWith, $file->getPathname());
- $packageVendorPath = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package->config->name
+ $packageVendorPath = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package->getName()
. DIRECTORY_SEPARATOR . $path;
$packageVendorPath = str_replace('/', DIRECTORY_SEPARATOR, $packageVendorPath);
$targetFile = str_replace($packageVendorPath, '', $targetFile);
} else {
- $namespacePath = $package->config->name;
- $replaceWith = $this->config->classmap_directory . DIRECTORY_SEPARATOR . $namespacePath;
+ $namespacePath = $package->getName();
+ $replaceWith = $this->config->getClassmapDirectory() . $namespacePath;
$targetFile = str_replace($this->workingDir, $replaceWith, $file->getPathname());
- $packageVendorPath = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package->config->name
+ $packageVendorPath = DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . $package->getName()
. DIRECTORY_SEPARATOR;
$packageVendorPath = str_replace('/', DIRECTORY_SEPARATOR, $packageVendorPath);
$targetFile = str_replace($packageVendorPath, DIRECTORY_SEPARATOR, $targetFile);
@@ -218,8 +219,6 @@ public function moveFile(Package $package, $autoloader, $file, $path = '')
* Deletes all the packages that are moved from the /vendor/ directory to
* prevent packages that are prefixed/namespaced from being used or
* influencing the output of the code. They just need to be gone.
- *
- * @return void
*/
protected function deletePackageVendorDirectories(): void
{
diff --git a/src/PackageFactory.php b/src/PackageFactory.php
new file mode 100644
index 00000000..da67d61e
--- /dev/null
+++ b/src/PackageFactory.php
@@ -0,0 +1,35 @@
+ */
+ public static array $cache = [];
+
+ public static function createPackage(
+ string $path,
+ stdClass $overrideAutoload = null,
+ bool $loadDependencies = true
+ ): Package {
+ if (isset(self::$cache[$path])) {
+ return self::$cache[$path];
+ }
+
+ $package = Package::loadFromFile($path);
+
+ if (! empty($overrideAutoload)) {
+ $package->setAutoload($overrideAutoload);
+ }
+
+ if ($loadDependencies) {
+ $package->loadDependencies();
+ }
+
+ self::$cache[$path] = $package;
+ return $package;
+ }
+}
diff --git a/src/PackageFinder.php b/src/PackageFinder.php
new file mode 100644
index 00000000..9f162c63
--- /dev/null
+++ b/src/PackageFinder.php
@@ -0,0 +1,93 @@
+config = $config;
+ }
+
+ public function getPackageBySlug(string $slug): ?Package
+ {
+ /**
+ * This case prevents issues where the requirements array can contain
+ * non-package like lines, for example: php or extensions.
+ */
+ if (!strstr($slug, '/')) {
+ return null;
+ }
+
+ if (empty($this->config)) {
+ throw new Exception("Config not set to find packages");
+ }
+
+ $packageDir = $this->config->getWorkingDir() . DIRECTORY_SEPARATOR . 'vendor'
+ . DIRECTORY_SEPARATOR . $slug . DIRECTORY_SEPARATOR;
+
+ if (! is_dir($packageDir)) {
+ throw new Exception("Couldn't load package based on provided slug: " . $slug);
+ }
+
+ $autoloaders = null;
+ $override_autoload = $this->config->getOverrideAutoload();
+ if ($override_autoload !== false && isset($override_autoload->$slug)) {
+ $autoloaders = $override_autoload->$slug;
+ }
+
+ return PackageFactory::createPackage($packageDir . 'composer.json', $autoloaders, true);
+ }
+
+ /**
+ * @param string[] $slugs
+ * @return Package[]
+ */
+ public function getPackagesBySlugs(array $slugs): array
+ {
+ $packages = array_map(function (string $slug) {
+ return $this->getPackageBySlug($slug);
+ }, $slugs);
+
+ return array_filter($packages, function ($package) {
+ return $package instanceof Package;
+ });
+ }
+
+ /**
+ * Loops through all dependencies and their dependencies and so on...
+ * will eventually return a list of all packages required by the full tree.
+ *
+ * @param Package[] $packages
+ *
+ * @return Package[]
+ */
+ public function findPackages(array $packages): array
+ {
+ foreach ($packages as $package) {
+ $dependencies = $package->getDependencies();
+
+ $package->registerDependencies($this->findPackages($dependencies));
+ $packages[$package->getName()] = $package;
+ }
+
+ return $packages;
+ }
+}
diff --git a/src/Replace/BaseReplacer.php b/src/Replace/BaseReplacer.php
index 442551e8..3298c5d6 100644
--- a/src/Replace/BaseReplacer.php
+++ b/src/Replace/BaseReplacer.php
@@ -11,9 +11,8 @@ abstract class BaseReplacer implements Replacer
/**
* @param Autoloader $autoloader
- * @return void
*/
- public function setAutoloader(Autoloader $autoloader)
+ public function setAutoloader(Autoloader $autoloader): void
{
$this->autoloader = $autoloader;
}
diff --git a/src/Replace/ClassmapReplacer.php b/src/Replace/ClassmapReplacer.php
index 2cc297ed..41323fa3 100644
--- a/src/Replace/ClassmapReplacer.php
+++ b/src/Replace/ClassmapReplacer.php
@@ -17,8 +17,6 @@ class ClassmapReplacer extends BaseReplacer
/**
* @param false|string $contents
- *
- * @return string
*/
public function replace($contents): string
{
@@ -29,16 +27,16 @@ public function replace($contents): string
return preg_replace_callback(
"
/ # Start the pattern
- namespace\s+[a-zA-Z0-9_\x7f-\xff\\\\]+[;{\s\n]{1}.*?(?=namespace|$)
- # Look for a preceeding namespace declaration, up until
+ namespace\s+[a-zA-Z0-9_\x7f-\xff\\\\]+[;{\s\n]{1}.*?(?=namespace|$)
+ # Look for a preceeding namespace declaration, up until
# a potential second namespace declaration
- | # if found, match that much before repeating the search
+ | # if found, match that much before repeating the search
# on the remainder of the string
(?:abstract\sclass|class|interface)\s+ # Look behind for class, abstract class, interface
- ([a-zA-Z0-9_\x7f-\xff]+) # Match the word until the first
+ ([a-zA-Z0-9_\x7f-\xff]+) # Match the word until the first
# non-classname-valid character
\s? # Allow a space after
- (?:{|extends|implements|\n) # Class declaration can be followed by {, extends,
+ (?:{|extends|implements|\n) # Class declaration can be followed by {, extends,
# implements, or a new line
/sx", // # dot matches newline, ignore whitespace in regex.
function ($matches) use ($contents) {
diff --git a/src/Replace/NamespaceReplacer.php b/src/Replace/NamespaceReplacer.php
index 711ec086..680fb413 100644
--- a/src/Replace/NamespaceReplacer.php
+++ b/src/Replace/NamespaceReplacer.php
@@ -14,10 +14,8 @@ class NamespaceReplacer extends BaseReplacer
/**
* @param string $contents The text to make replacements in.
* @param null $file Only used in ClassmapReplacer (for recording which files were changed).
- *
- * @return string The updated text.
*/
- public function replace($contents, $file = null)
+ public function replace($contents, $file = null): string
{
$searchNamespace = preg_quote($this->autoloader->getSearchNamespace(), '/');
$dependencyNamespace = preg_quote($this->dep_namespace, '/');
diff --git a/src/Replacer.php b/src/Replacer.php
index 57bd4e29..bc2668b2 100644
--- a/src/Replacer.php
+++ b/src/Replacer.php
@@ -3,9 +3,10 @@
namespace CoenJacobs\Mozart;
use CoenJacobs\Mozart\Composer\Autoload\Autoloader;
-use CoenJacobs\Mozart\Composer\Autoload\Classmap;
use CoenJacobs\Mozart\Composer\Autoload\NamespaceAutoloader;
-use CoenJacobs\Mozart\Composer\Package;
+use CoenJacobs\Mozart\Config\Classmap;
+use CoenJacobs\Mozart\Config\Mozart;
+use CoenJacobs\Mozart\Config\Package;
use CoenJacobs\Mozart\Replace\ClassmapReplacer;
use CoenJacobs\Mozart\Replace\NamespaceReplacer;
use League\Flysystem\Local\LocalFilesystemAdapter;
@@ -21,7 +22,7 @@ class Replacer
/** @var string */
protected $targetDir;
- /** @var \stdClass */
+ /** @var Mozart */
protected $config;
/** @var array */
@@ -30,11 +31,11 @@ class Replacer
/** @var Filesystem */
protected $filesystem;
- public function __construct($workingDir, $config)
+ public function __construct(string $workingDir, Mozart $config)
{
$this->workingDir = $workingDir;
- $this->targetDir = $config->dep_directory;
- $this->config = $config;
+ $this->config = $config;
+ $this->targetDir = $this->config->getDepDirectory();
$adapter = new LocalFilesystemAdapter(
$this->workingDir
@@ -44,20 +45,25 @@ public function __construct($workingDir, $config)
$this->filesystem = new Filesystem($adapter);
}
+ /**
+ * @param Package[] $packages
+ */
+ public function replacePackages($packages): void
+ {
+ foreach ($packages as $package) {
+ $this->replacePackages($package->getDependencies());
+ $this->replacePackage($package);
+ }
+ }
+
public function replacePackage(Package $package): void
{
- foreach ($package->autoloaders as $autoloader) {
+ foreach ($package->getAutoloaders() as $autoloader) {
$this->replacePackageByAutoloader($package, $autoloader);
}
}
- /**
- * @param $targetFile
- * @param $autoloader
- *
- * @return void
- */
- public function replaceInFile($targetFile, Autoloader $autoloader): void
+ public function replaceInFile(string $targetFile, Autoloader $autoloader): void
{
$targetFile = str_replace($this->workingDir, '', $targetFile);
try {
@@ -66,16 +72,16 @@ public function replaceInFile($targetFile, Autoloader $autoloader): void
return;
}
- if (empty($contents) || false === $contents) {
+ if (!$contents) {
return;
}
if ($autoloader instanceof NamespaceAutoloader) {
$replacer = new NamespaceReplacer();
- $replacer->dep_namespace = $this->config->dep_namespace;
+ $replacer->dep_namespace = $this->config->getDependencyNamespace();
} else {
$replacer = new ClassmapReplacer();
- $replacer->classmap_prefix = $this->config->classmap_prefix;
+ $replacer->classmap_prefix = $this->config->getClassmapPrefix();
}
$replacer->setAutoloader($autoloader);
@@ -88,23 +94,19 @@ public function replaceInFile($targetFile, Autoloader $autoloader): void
$this->filesystem->write($targetFile, $contents);
}
- /**
- * @param Package $package
- * @param $autoloader
- *
- * @return void
- */
- public function replacePackageByAutoloader(Package $package, Composer\Autoload\Autoloader $autoloader): void
+ public function replacePackageByAutoloader(Package $package, Autoloader $autoloader): void
{
+ if ($this->config->isExcludedPackage($package)) {
+ return;
+ }
+
if ($autoloader instanceof NamespaceAutoloader) {
$source_path = $this->workingDir . $this->targetDir
- . str_replace('\\', DIRECTORY_SEPARATOR, $autoloader->namespace)
- . DIRECTORY_SEPARATOR;
+ . str_replace('\\', DIRECTORY_SEPARATOR, $autoloader->getNamespace());
$this->replaceInDirectory($autoloader, $source_path);
} elseif ($autoloader instanceof Classmap) {
$finder = new Finder();
- $source_path = $this->workingDir . $this->config->classmap_directory . DIRECTORY_SEPARATOR
- . $package->config->name;
+ $source_path = $this->workingDir . $this->config->getClassmapDirectory() . $package->getName();
$finder->files()->in($source_path);
foreach ($finder as $foundFile) {
@@ -117,12 +119,6 @@ public function replacePackageByAutoloader(Package $package, Composer\Autoload\A
}
}
- /**
- * @param $autoloader
- * @param $directory
- *
- * @return void
- */
public function replaceParentClassesInDirectory(string $directory): void
{
if (count($this->replacedClasses)===0) {
@@ -145,7 +141,7 @@ public function replaceParentClassesInDirectory(string $directory): void
continue;
}
- if (empty($contents) || false === $contents) {
+ if (!$contents) {
continue;
}
@@ -171,12 +167,6 @@ function ($matches) use ($replacement) {
}
}
- /**
- * @param $autoloader
- * @param $directory
- *
- * @return void
- */
public function replaceInDirectory(NamespaceAutoloader $autoloader, string $directory): void
{
$finder = new Finder();
@@ -195,19 +185,18 @@ public function replaceInDirectory(NamespaceAutoloader $autoloader, string $dire
* Replace everything in parent package, based on the dependency package.
* This is done to ensure that package A (which requires package B), is also
* updated with the replacements being made in package B.
- *
- * @param Package $package
- * @param Package $parent
- *
- * @return void
*/
public function replaceParentPackage(Package $package, Package $parent): void
{
- foreach ($parent->autoloaders as $parentAutoloader) {
- foreach ($package->autoloaders as $autoloader) {
+ if ($this->config->isExcludedPackage($package)) {
+ return;
+ }
+
+ foreach ($parent->getAutoloaders() as $parentAutoloader) {
+ foreach ($package->getAutoloaders() as $autoloader) {
if ($parentAutoloader instanceof NamespaceAutoloader) {
$namespace = str_replace('\\', DIRECTORY_SEPARATOR, $parentAutoloader->namespace);
- $directory = $this->workingDir . $this->config->dep_directory . $namespace
+ $directory = $this->workingDir . $this->config->getDepDirectory() . $namespace
. DIRECTORY_SEPARATOR;
if ($autoloader instanceof NamespaceAutoloader) {
@@ -217,7 +206,8 @@ public function replaceParentPackage(Package $package, Package $parent): void
$this->replaceParentClassesInDirectory($directory);
}
} else {
- $directory = $this->workingDir . $this->config->classmap_directory . $parent->config->name;
+ $directory = $this->workingDir .
+ $this->config->getClassmapDirectory() . $parent->getName();
if ($autoloader instanceof NamespaceAutoloader) {
$this->replaceInDirectory($autoloader, $directory);
@@ -229,4 +219,47 @@ public function replaceParentPackage(Package $package, Package $parent): void
}
}
}
+
+ /**
+ * Get an array containing all the dependencies and dependencies
+ * @param Package $package
+ * @param Package[] $dependencies
+ * @return Package[]
+ */
+ private function getAllDependenciesOfPackage(Package $package, $dependencies = []): array
+ {
+ if (empty($package->getDependencies())) {
+ return $dependencies;
+ }
+
+ foreach ($package->getDependencies() as $dependency) {
+ $dependencies[] = $dependency;
+ }
+
+ foreach ($package->getDependencies() as $dependency) {
+ $dependencies = $this->getAllDependenciesOfPackage($dependency, $dependencies);
+ }
+
+ return $dependencies;
+ }
+
+ /**
+ * @param Package[] $packages
+ */
+ public function replaceParentInTree(array $packages): void
+ {
+ foreach ($packages as $package) {
+ if ($this->config->isExcludedPackage($package)) {
+ continue;
+ }
+
+ $dependencies = $this->getAllDependenciesOfPackage($package);
+
+ foreach ($dependencies as $dependency) {
+ $this->replaceParentPackage($dependency, $package);
+ }
+
+ $this->replaceParentInTree($package->getDependencies());
+ }
+ }
}
diff --git a/tests/Config/ConfigMapperTest.php b/tests/Config/ConfigMapperTest.php
new file mode 100644
index 00000000..42e1ed7a
--- /dev/null
+++ b/tests/Config/ConfigMapperTest.php
@@ -0,0 +1,23 @@
+assertInstanceOf(Package::class, $package);
+ $this->assertInstanceOf(Mozart::class, $package->getExtra()->getMozart());
+ $this->assertCount(4, $package->autoload->getAutoloaders());
+ }
+}
diff --git a/tests/Config/config-mapper-test.json b/tests/Config/config-mapper-test.json
new file mode 100644
index 00000000..b043f34b
--- /dev/null
+++ b/tests/Config/config-mapper-test.json
@@ -0,0 +1,30 @@
+{
+ "require": {
+ },
+ "autoload": {
+ "psr-0": {
+ "Mozart\\RandomDir\\": "old_files/",
+ "Mozart\\Multiples\\": [ "another_dir", "more_dirs" ]
+ },
+ "psr-4": {
+ "Mozart\\TestProject\\": "src/",
+ "Mozart\\MultipleDirs\\": [ "packages/test/src/", "packages/more/src/" ]
+ }
+ },
+ "extra": {
+ "mozart": {
+ "dep_namespace": "Mozart\\TestProject\\Dependencies",
+ "dep_directory": "/src/dependencies",
+ "classmap_directory": "/classes/",
+ "classmap_prefix": "MozartDependency_",
+ "packages": [
+ "pimple/pimple"
+ ],
+ "excluded_packages": [
+ ],
+ "override_autoload": {
+ },
+ "delete_vendor_directories": true
+ }
+ }
+ }
diff --git a/tests/Console/Commands/ComposeTest.php b/tests/Console/Commands/ComposeTest.php
index ea283cfc..325ea3bc 100644
--- a/tests/Console/Commands/ComposeTest.php
+++ b/tests/Console/Commands/ComposeTest.php
@@ -39,7 +39,6 @@ public function setUp(): void
#[Test]
public function it_fails_gracefully_when_composer_json_absent(): void
{
-
$inputInterfaceMock = $this->createMock(InputInterface::class);
$outputInterfaceMock = $this->createMock(OutputInterface::class);
@@ -66,7 +65,6 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock)
#[Test]
public function it_handles_malformed_json_with_grace(): void
{
-
$badComposerJson = '{ "name": "coenjacobs/mozart", }';
file_put_contents(__DIR__ . '/composer.json', $badComposerJson);
@@ -97,7 +95,6 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock)
#[Test]
public function it_handles_absent_extra_config_with_grace(): void
{
-
$badComposerJson = '{ "name": "coenjacobs/mozart" }';
file_put_contents(__DIR__ . '/composer.json', $badComposerJson);
@@ -129,7 +126,6 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock)
#[Test]
public function it_handles_malformed_extra_config_with_grace(): void
{
-
$badComposerJson = '{ "name": "coenjacobs/mozart", "extra": [] }';
file_put_contents(__DIR__ . '/composer.json', $badComposerJson);
@@ -160,7 +156,6 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock)
#[Test]
public function it_handles_absent_mozart_config_with_grace(): void
{
-
$badComposerJson = '{ "name": "coenjacobs/mozart", "extra": { "moozart": {} } }';
file_put_contents(__DIR__ . '/composer.json', $badComposerJson);
@@ -193,7 +188,6 @@ public function __construct($inputInterfaceMock, $outputInterfaceMock)
#[Test]
public function it_handles_malformed_mozart_config__with_grace(): void
{
-
$badComposerJson = '{ "name": "coenjacobs/mozart", "extra": { "mozart": [] } }';
file_put_contents(__DIR__ . '/composer.json', $badComposerJson);
diff --git a/tests/Integration/ExcludedPackagesTest.php b/tests/Integration/ExcludedPackagesTest.php
new file mode 100644
index 00000000..646f5dba
--- /dev/null
+++ b/tests/Integration/ExcludedPackagesTest.php
@@ -0,0 +1,109 @@
+testsWorkingDir = __DIR__ . '/temptestdir';
+ if (!file_exists($this->testsWorkingDir)) {
+ mkdir($this->testsWorkingDir);
+ }
+ }
+
+ /**
+ * Verifies that the explicitely excluded packages from the Mozart config
+ * are _not_ being moved to the provided dependency directory and the files
+ * will stay present in the vendor directory. At the same time, the other
+ * package is being moved to the dependency directory and after that the
+ * originating directory in the vendor directory is deleted (as the
+ * `delete_vendor_directories` parameter is set to `true`).
+ *
+ * @test
+ */
+ #[Test]
+ public function it_excludes_moving_specified_packages(): void
+ {
+ copy(__DIR__ . '/excluded-packages.json', $this->testsWorkingDir . '/composer.json');
+
+ chdir($this->testsWorkingDir);
+
+ exec('composer update');
+
+ $inputInterfaceMock = $this->createMock(InputInterface::class);
+ $outputInterfaceMock = $this->createMock(OutputInterface::class);
+
+ $mozartCompose = new Compose();
+
+ $result = $mozartCompose->run($inputInterfaceMock, $outputInterfaceMock);
+ $this->assertEquals(0, $result);
+
+ $this->assertDirectoryDoesNotExist($this->testsWorkingDir . '/vendor/pimple/pimple');
+ $this->assertDirectoryExists($this->testsWorkingDir . '/src/dependencies/Pimple');
+
+ $this->assertDirectoryExists($this->testsWorkingDir . '/vendor/psr/container');
+ $this->assertDirectoryDoesNotExist($this->testsWorkingDir . '/src/dependencies/Psr');
+ }
+
+ /**
+ * Verifies that the excluded package `psr/container` is _not_ having its
+ * classes replaced in the implementing `pimple/pimple` package when the
+ * former is explicitely excluded and the latter is added to the list of
+ * packages for Mozart to rewrite.
+ *
+ * @test
+ */
+ #[Test]
+ public function it_excludes_replacing_classes_from_specified_packages(): void
+ {
+ copy(__DIR__ . '/excluded-packages.json', $this->testsWorkingDir . '/composer.json');
+
+ chdir($this->testsWorkingDir);
+
+ exec('composer update');
+
+ $inputInterfaceMock = $this->createMock(InputInterface::class);
+ $outputInterfaceMock = $this->createMock(OutputInterface::class);
+
+ $mozartCompose = new Compose();
+
+ $result = $mozartCompose->run($inputInterfaceMock, $outputInterfaceMock);
+ $this->assertEquals(0, $result);
+
+ $testFile = file_get_contents($this->testsWorkingDir . '/src/dependencies/Pimple/Psr11/Container.php');
+ $this->assertStringContainsString('namespace Mozart\TestProject\Dependencies\Pimple\Psr11;', $testFile);
+ $this->assertStringContainsString('use Mozart\TestProject\Dependencies\Pimple\Container as PimpleContainer;', $testFile);
+ $this->assertStringContainsString('use Psr\Container\ContainerInterface;', $testFile);
+ }
+
+ public function tearDown(): void
+ {
+ parent::tearDown();
+
+ $dir = $this->testsWorkingDir;
+
+ $it = new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS);
+ $files = new RecursiveIteratorIterator(
+ $it,
+ RecursiveIteratorIterator::CHILD_FIRST
+ );
+ foreach ($files as $file) {
+ if ($file->isDir()) {
+ rmdir($file->getRealPath());
+ } else {
+ unlink($file->getRealPath());
+ }
+ }
+ rmdir($dir);
+ chdir(__DIR__);
+ }
+}
diff --git a/tests/Integration/excluded-packages.json b/tests/Integration/excluded-packages.json
new file mode 100644
index 00000000..f0165703
--- /dev/null
+++ b/tests/Integration/excluded-packages.json
@@ -0,0 +1,26 @@
+{
+ "require": {
+ "pimple/pimple": "^3.5"
+ },
+ "autoload": {
+ "psr-4": {
+ "Mozart\\TestProject\\": "src/"
+ }
+ },
+ "extra": {
+ "mozart": {
+ "dep_namespace": "Mozart\\TestProject\\Dependencies",
+ "dep_directory": "/src/dependencies",
+ "classmap_directory": "/classes/",
+ "classmap_prefix": "MozartDependency_",
+ "packages": [
+ "pimple/pimple"
+ ],
+ "excluded_packages": [
+ "psr/container"
+ ],
+ "override_autoload": {},
+ "delete_vendor_directories": true
+ }
+ }
+}
diff --git a/tests/MoverTest.php b/tests/MoverTest.php
index 4577cc90..8945f49e 100644
--- a/tests/MoverTest.php
+++ b/tests/MoverTest.php
@@ -1,7 +1,8 @@
extra->mozart settings
*
- * @var stdClass
+ * @var Mozart
*/
protected $config;
@@ -38,23 +38,23 @@ public function setUp(): void
mkdir($this->testsWorkingDir);
}
- $config = new class() {
- };
- $config->dep_directory = "/dep_directory/";
- $config->classmap_directory = "/classmap_directory/";
- $config->packages = array(
- "pimple/pimple",
- "ezyang/htmlpurifier"
- );
-
$pimpleAutoload = json_decode("{ \"psr-0\" : { \"Pimple\" : [ \"src/\" ] } }");
$htmlpurifierAutoload = json_decode("{ \"classmap\" : { \"Pimple\" => [ \"library/\" ] } }");
- $config->override_autoload = array();
- $config->override_autoload["pimple/pimple"] = $pimpleAutoload;
- $config->override_autoload["ezyang/htmlpurifier"] = $htmlpurifierAutoload;
+ $configArgs = array(
+ 'dep_directory' => "/dep_directory/",
+ 'classmap_directory' => "/classmap_directory/",
+ 'packages' => array(
+ "pimple/pimple",
+ "ezyang/htmlpurifier",
+ ),
+ 'override_autoload' => array(
+ 'pimple/pimple' => $pimpleAutoload,
+ 'ezyang/htmlpurifier' => $htmlpurifierAutoload,
+ ),
+ );
- $this->config = $config;
+ $this->config = Mozart::loadFromString( json_encode($configArgs) );
}
/**
@@ -72,9 +72,9 @@ public function it_creates_absent_dirs(): void
$mover->deleteTargetDirs($packages);
$this->assertTrue(file_exists($this->testsWorkingDir . DIRECTORY_SEPARATOR
- . $this->config->dep_directory));
+ . $this->config->getDepDirectory()));
$this->assertTrue(file_exists($this->testsWorkingDir . DIRECTORY_SEPARATOR
- . $this->config->classmap_directory));
+ . $this->config->getClassmapDirectory()));
}
/**
@@ -87,15 +87,15 @@ public function it_is_unpertrubed_by_existing_dirs(): void
{
$mover = new Mover($this->testsWorkingDir, $this->config);
- if (!file_exists($this->testsWorkingDir . $this->config->dep_directory)) {
- mkdir($this->testsWorkingDir . $this->config->dep_directory);
+ if (!file_exists($this->testsWorkingDir . $this->config->getDepDirectory())) {
+ mkdir($this->testsWorkingDir . $this->config->getDepDirectory());
}
- if (!file_exists($this->testsWorkingDir . $this->config->classmap_directory)) {
- mkdir($this->testsWorkingDir . $this->config->classmap_directory);
+ if (!file_exists($this->testsWorkingDir . $this->config->getClassmapDirectory())) {
+ mkdir($this->testsWorkingDir . $this->config->getClassmapDirectory());
}
- $this->assertDirectoryExists($this->testsWorkingDir . $this->config->dep_directory);
- $this->assertDirectoryExists($this->testsWorkingDir . $this->config->classmap_directory);
+ $this->assertDirectoryExists($this->testsWorkingDir . $this->config->getDepDirectory());
+ $this->assertDirectoryExists($this->testsWorkingDir . $this->config->getClassmapDirectory());
$packages = array();
@@ -117,16 +117,14 @@ public function it_is_unpertrubed_by_existing_dirs(): void
#[Test]
public function it_deletes_subdirs_for_packages_about_to_be_moved(): void
{
- $mover = new Mover($this->testsWorkingDir, $this->config);
-
- mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->dep_directory);
- mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->classmap_directory);
+ mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->getDepDirectory());
+ mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->getClassmapDirectory());
- mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->dep_directory . 'Pimple');
- mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->classmap_directory . 'ezyang');
+ mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->getDepDirectory() . 'Pimple');
+ mkdir($this->testsWorkingDir . DIRECTORY_SEPARATOR . $this->config->getClassmapDirectory() . 'ezyang');
$packages = array();
- foreach ($this->config->packages as $packageString) {
+ foreach ($this->config->getPackages() as $packageString) {
$testDummyComposerDir = $this->testsWorkingDir . DIRECTORY_SEPARATOR . 'vendor'
. DIRECTORY_SEPARATOR . $packageString;
@mkdir($testDummyComposerDir, 0777, true);
@@ -134,15 +132,20 @@ public function it_deletes_subdirs_for_packages_about_to_be_moved(): void
$testDummyComposerContents = json_encode(new stdClass());
file_put_contents($testDummyComposerPath, $testDummyComposerContents);
- $parsedPackage = new Package($testDummyComposerDir, $this->config->override_autoload[$packageString]);
- $parsedPackage->findAutoloaders();
+
+ $overrideAutoload = $this->config->getOverrideAutoload();
+ if ( ! empty( $overrideAutoload ) ) {
+ $overrideAutoload = $overrideAutoload->getByKey( $packageString );
+ }
+ $parsedPackage = PackageFactory::createPackage($testDummyComposerPath, $overrideAutoload);
$packages[] = $parsedPackage;
}
+ $mover = new Mover($this->testsWorkingDir, $this->config);
$mover->deleteTargetDirs($packages);
- $this->assertDirectoryDoesNotExist($this->testsWorkingDir . $this->config->dep_directory . 'Pimple');
- $this->assertDirectoryDoesNotExist($this->testsWorkingDir . $this->config->dep_directory . 'ezyang');
+ $this->assertDirectoryDoesNotExist($this->testsWorkingDir . $this->config->getDepDirectory() . 'Pimple');
+ $this->assertDirectoryDoesNotExist($this->testsWorkingDir . $this->config->getDepDirectory() . 'ezyang');
}
/**
diff --git a/tests/replacers/NamespaceReplacerTest.php b/tests/replacers/NamespaceReplacerTest.php
index f5856ede..b0651bc7 100644
--- a/tests/replacers/NamespaceReplacerTest.php
+++ b/tests/replacers/NamespaceReplacerTest.php
@@ -1,7 +1,7 @@