diff --git a/build/travis/script.sh b/build/travis/script.sh index 2eaa08ea..859985b9 100644 --- a/build/travis/script.sh +++ b/build/travis/script.sh @@ -21,4 +21,4 @@ if [[ "$PHPUNIT" = true ]]; then } fi done -fi +fi \ No newline at end of file diff --git a/src/Automatic/AbstractConfigurator.php b/src/Automatic/AbstractConfigurator.php index 83592f66..e2d8bc03 100644 --- a/src/Automatic/AbstractConfigurator.php +++ b/src/Automatic/AbstractConfigurator.php @@ -7,6 +7,7 @@ use Narrowspark\Automatic\Common\Contract\Configurator as ConfiguratorContract; use Narrowspark\Automatic\Common\Contract\Exception\InvalidArgumentException; use Narrowspark\Automatic\Common\Contract\Package as PackageContract; +use Narrowspark\Automatic\Common\Contract\Resettable as ResettableContract; abstract class AbstractConfigurator { @@ -130,11 +131,9 @@ public function getConfigurators(): array } /** - * Clear all configurators. - * - * @return void + * {@inheritdoc} */ - public function clear(): void + public function reset(): void { $this->configurators = []; } diff --git a/src/Automatic/Automatic.php b/src/Automatic/Automatic.php index 049cd6b4..0758ce58 100644 --- a/src/Automatic/Automatic.php +++ b/src/Automatic/Automatic.php @@ -34,6 +34,7 @@ use Composer\Script\ScriptEvents; use FilesystemIterator; use Narrowspark\Automatic\Common\Contract\Configurator as ConfiguratorContract; +use Narrowspark\Automatic\Common\Contract\Resettable as ResettableContract; use Narrowspark\Automatic\Common\Contract\Exception\InvalidArgumentException; use Narrowspark\Automatic\Common\Contract\Exception\RuntimeException; use Narrowspark\Automatic\Common\Contract\Package as PackageContract; @@ -52,7 +53,7 @@ use ReflectionClass; use Symfony\Component\Console\Input\ArgvInput; -class Automatic implements PluginInterface, EventSubscriberInterface +class Automatic implements PluginInterface, EventSubscriberInterface, ResettableContract { use ExpandTargetDirTrait; use GetGenericPropertyReaderTrait; @@ -295,12 +296,12 @@ public function onPostUpdate(Event $event, array $operations = []): void $this->operations = $operations; } + /** @var \Narrowspark\Automatic\Lock $lock */ + /** @var \Composer\IO\IOInterface $io */ $automaticOptions = $this->container->get('composer-extra')[Util::COMPOSER_EXTRA_KEY]; $allowInstall = $automaticOptions['allow-auto-install'] ?? false; $packages = $this->container->get(OperationsResolver::class)->resolve($this->operations); - /** @var \Narrowspark\Automatic\Lock $lock */ $lock = $this->container->get(Lock::class); - /** @var \Composer\IO\IOInterface $io */ $io = $this->container->get(IOInterface::class); $io->writeError(\sprintf( @@ -524,6 +525,15 @@ public function onFileDownload(PreFileDownloadEvent $event): void } } + /** + * {@inheritdoc} + */ + public function reset(): void + { + $this->operations = []; + $this->container->get(Configurator::class)->reset(); + } + /** * Add found legacy tags to the tags manager. * @@ -661,7 +671,7 @@ private function doActionOnPackageOperation(PackageContract $package): void $this->doUninstall($package, $packageConfigurator); } - $packageConfigurator->clear(); + $packageConfigurator->reset(); } /** @@ -679,7 +689,15 @@ private function doInstall(PackageContract $package, PackageConfigurator $packag /** @var \Narrowspark\Automatic\Lock $lock */ $lock = $this->container->get(Lock::class); - $this->writeScriptExtenderToLock($package, $lock); + if ($package->hasConfig(ScriptExecutor::TYPE)) { + $extenders = []; + + foreach ((array) $package->getConfig(ScriptExecutor::TYPE) as $extender) { + $extenders[$extender] = $package->getName() . \DIRECTORY_SEPARATOR . ''; + } + + $lock->addSub(ScriptExecutor::TYPE, $package->getName(), $extenders); + } /** @var \Narrowspark\Automatic\Configurator $configurator */ $configurator = $this->container->get(Configurator::class); @@ -697,13 +715,7 @@ private function doInstall(PackageContract $package, PackageConfigurator $packag $this->postInstallOutput[] = ''; } - $lock->add( - self::LOCK_PACKAGES, - \array_merge( - (array) $lock->get(self::LOCK_PACKAGES), - [$package->getName() => $package->toArray()] - ) - ); + $lock->addSub(self::LOCK_PACKAGES, $package->getName(), $package->toArray()); } /** @@ -730,54 +742,10 @@ private function doUninstall(PackageContract $package, PackageConfigurator $pack $lock = $this->container->get(Lock::class); if ($package->hasConfig(ScriptExecutor::TYPE)) { - $extenders = (array) $lock->get(ScriptExecutor::TYPE); - - /** @var \Narrowspark\Automatic\Common\Contract\ScriptExtender $extender */ - foreach ((array) $package->getConfig(ScriptExecutor::TYPE) as $extender) { - $type = $extender::getType(); - - if (isset($extenders[$type])) { - unset($extenders[$type]); - } - } - - $lock->add(ScriptExecutor::TYPE, $extenders); + $lock->remove(ScriptExecutor::TYPE, $package->getName()); } - $lock->remove($package->getName()); - } - - /** - * Looks if the package has a extra section for script extender. - * - * @param \Narrowspark\Automatic\Common\Contract\Package $package - * @param \Narrowspark\Automatic\Lock $lock - * - * @throws \Narrowspark\Automatic\Common\Contract\Exception\InvalidArgumentException - * - * @return void - */ - private function writeScriptExtenderToLock(PackageContract $package, Lock $lock): void - { - if ($package->hasConfig(ScriptExecutor::TYPE)) { - $extenders = (array) $lock->get(ScriptExecutor::TYPE); - - foreach ((array) $package->getConfig(ScriptExecutor::TYPE) as $extender) { - /** @var \Narrowspark\Automatic\Common\Contract\ScriptExtender $extender */ - if (isset($extenders[$extender::getType()])) { - throw new InvalidArgumentException(\sprintf('Script executor extender with the name [%s] already exists.', $extender::getType())); - } - - if (! \is_subclass_of($extender, ScriptExtenderContract::class)) { - throw new InvalidArgumentException(\sprintf('The class [%s] must implement the interface [%s].', $extender, ScriptExtenderContract::class)); - } - - /** @var \Narrowspark\Automatic\Common\Contract\ScriptExtender $extender */ - $extenders[$extender::getType()] = $extender; - } - - $lock->add(ScriptExecutor::TYPE, $extenders); - } + $lock->remove(self::LOCK_PACKAGES, $package->getName()); } /** @@ -794,20 +762,17 @@ private function runSkeletonGenerator(): void $lock->read(); - if ($lock->has(SkeletonInstaller::LOCK_KEY) && $this->container->get(IOInterface::class)->isInteractive()) { - // Clear old operations if a skeleton is generated. - $this->operations = []; - $this->container->get(Configurator::class)->clear(); + if ($lock->has(SkeletonInstaller::LOCK_KEY)) { + $this->reset(); /** @var \Narrowspark\Automatic\SkeletonGenerator $skeletonGenerator */ $skeletonGenerator = $this->container->get(SkeletonGenerator::class); - $skeletonGenerator->run(); - - $skeletonGenerator->remove(); + $skeletonGenerator->run() + ->selfRemove(); } - $lock->clear(); + $lock->reset(); } /** diff --git a/src/Automatic/PathClassLoader.php b/src/Automatic/ClassLoader.php similarity index 91% rename from src/Automatic/PathClassLoader.php rename to src/Automatic/ClassLoader.php index 7af14b9a..765299ae 100644 --- a/src/Automatic/PathClassLoader.php +++ b/src/Automatic/ClassLoader.php @@ -2,9 +2,10 @@ declare(strict_types=1); namespace Narrowspark\Automatic; +use Narrowspark\Automatic\Common\Contract\Resettable as ResettableContract; use Symfony\Component\Finder\Finder; -final class PathClassLoader +final class ClassLoader implements ResettableContract { /** * List of traits. @@ -49,6 +50,10 @@ public function find($dirs): void ->in($dirs) ->name('*.php'); + if ($this->filter !== null) { + $finder = $finder->filter($this->filter); + } + /** @var \SplFileInfo $file */ foreach ($finder as $file) { $realPath = (string) $file->getRealPath(); @@ -133,6 +138,17 @@ public function getAll(): array ); } + /** + * {@inheritdoc} + */ + public function reset(): void + { + $this->interfaces = []; + $this->traits = []; + $this->abstractClasses = []; + $this->classes = []; + } + /** * Find the namespace in the tokens starting at a given key. * diff --git a/src/Automatic/Configurator.php b/src/Automatic/Configurator.php index 3af217f5..db955963 100644 --- a/src/Automatic/Configurator.php +++ b/src/Automatic/Configurator.php @@ -32,7 +32,7 @@ final class Configurator extends AbstractConfigurator /** * {@inheritdoc} */ - public function clear(): void + public function reset(): void { $this->configurators = []; $this->cache = []; diff --git a/src/Automatic/Container.php b/src/Automatic/Container.php index 7e1390de..b45c3aea 100644 --- a/src/Automatic/Container.php +++ b/src/Automatic/Container.php @@ -87,7 +87,7 @@ public function __construct(Composer $composer, IOInterface $io) $container->get(IOInterface::class), $container->get(Composer::class), $container->get(Lock::class), - new PathClassLoader() + new ClassLoader() ); }, SkeletonInstaller::class => static function (Container $container) { @@ -95,7 +95,7 @@ public function __construct(Composer $composer, IOInterface $io) $container->get(IOInterface::class), $container->get(Composer::class), $container->get(Lock::class), - new PathClassLoader() + new ClassLoader() ); }, Configurator::class => static function (Container $container) { @@ -150,8 +150,8 @@ public function __construct(Composer $composer, IOInterface $io) $container->get('composer-extra') ); - $scriptExecutor->addExtender(ScriptExtender::class); - $scriptExecutor->addExtender(PhpScriptExtender::class); + $scriptExecutor->addExtender(ScriptExtender::getType(), ScriptExtender::class); + $scriptExecutor->addExtender(PhpScriptExtender::getType(),PhpScriptExtender::class); return $scriptExecutor; }, diff --git a/src/Automatic/Installer/AbstractInstaller.php b/src/Automatic/Installer/AbstractInstaller.php index f8c86177..11ba58aa 100644 --- a/src/Automatic/Installer/AbstractInstaller.php +++ b/src/Automatic/Installer/AbstractInstaller.php @@ -8,9 +8,9 @@ use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; use Narrowspark\Automatic\Automatic; +use Narrowspark\Automatic\ClassLoader; use Narrowspark\Automatic\Common\Contract\Exception\UnexpectedValueException; use Narrowspark\Automatic\Lock; -use Narrowspark\Automatic\PathClassLoader; abstract class AbstractInstaller extends LibraryInstaller { @@ -34,19 +34,19 @@ abstract class AbstractInstaller extends LibraryInstaller /** * A path class loader instance. * - * @var \Narrowspark\Automatic\PathClassLoader + * @var \Narrowspark\Automatic\ClassLoader */ protected $loader; /** * Create a new Installer instance. * - * @param \Composer\IO\IOInterface $io - * @param \Composer\Composer $composer - * @param \Narrowspark\Automatic\Lock $lock - * @param \Narrowspark\Automatic\PathClassLoader $loader + * @param \Composer\IO\IOInterface $io + * @param \Composer\Composer $composer + * @param \Narrowspark\Automatic\Lock $lock + * @param \Narrowspark\Automatic\ClassLoader $loader */ - public function __construct(IOInterface $io, Composer $composer, Lock $lock, PathClassLoader $loader) + public function __construct(IOInterface $io, Composer $composer, Lock $lock, ClassLoader $loader) { parent::__construct($io, $composer, static::TYPE); @@ -124,10 +124,17 @@ protected function findClasses(array $autoload, PackageInterface $package): ?arr { $name = $package->getPrettyName(); $classes = []; - - $psr4 = \array_map(function ($path) use ($name) { - return \rtrim(\rtrim($this->vendorDir, '/') . \DIRECTORY_SEPARATOR . $name . \DIRECTORY_SEPARATOR . $path, '/'); - }, (array) $autoload['psr-4']); + $psr4 = []; + + foreach ((array) $autoload['psr-4'] as $path) { + if (\is_array($path)) { + foreach ($path as $p) { + $psr4[] = \rtrim($this->vendorDir . \DIRECTORY_SEPARATOR . $name . \DIRECTORY_SEPARATOR . $p, '/'); + } + } else { + $psr4[] = \rtrim($this->vendorDir . \DIRECTORY_SEPARATOR . $name . \DIRECTORY_SEPARATOR . $path, '/'); + } + } $this->loader->find($psr4); diff --git a/src/Automatic/Lock.php b/src/Automatic/Lock.php index 205cf1d1..ab91e9d3 100644 --- a/src/Automatic/Lock.php +++ b/src/Automatic/Lock.php @@ -3,8 +3,9 @@ namespace Narrowspark\Automatic; use Composer\Json\JsonFile; +use Narrowspark\Automatic\Common\Contract\Resettable as ResettableContract; -class Lock +class Lock implements ResettableContract { /** * Instance of JsonFile. @@ -141,7 +142,7 @@ public function write(): void $this->json->write($this->lock); - $this->clear(); + $this->reset(); } /** @@ -159,11 +160,9 @@ public function read(): array } /** - * Clear the lock. - * - * @return void + * {@inheritdoc} */ - public function clear(): void + public function reset(): void { $this->lock = []; } diff --git a/src/Automatic/ScriptExecutor.php b/src/Automatic/ScriptExecutor.php index 25594faa..c25dd82f 100644 --- a/src/Automatic/ScriptExecutor.php +++ b/src/Automatic/ScriptExecutor.php @@ -6,6 +6,8 @@ use Composer\EventDispatcher\ScriptExecutionException; use Composer\IO\IOInterface; use Composer\Util\ProcessExecutor; +use Narrowspark\Automatic\Common\Contract\Exception\InvalidArgumentException; +use Narrowspark\Automatic\Common\Contract\ScriptExtender as ScriptExtenderContract; use Narrowspark\Automatic\Common\Traits\ExpandTargetDirTrait; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Output\StreamOutput; @@ -47,7 +49,7 @@ final class ScriptExecutor /** * A list of the registered extenders. * - * @var \Narrowspark\Automatic\Common\Contract\ScriptExtender[] + * @var string[] */ private $extenders = []; @@ -59,8 +61,12 @@ final class ScriptExecutor * @param \Composer\Util\ProcessExecutor $executor * @param array $options */ - public function __construct(Composer $composer, IOInterface $io, ProcessExecutor $executor, array $options) - { + public function __construct( + Composer $composer, + IOInterface $io, + ProcessExecutor $executor, + array $options + ) { $this->composer = $composer; $this->io = $io; $this->executor = $executor; @@ -70,14 +76,22 @@ public function __construct(Composer $composer, IOInterface $io, ProcessExecutor /** * Register a cmd extender. * + * @param string $name * @param string $extender * * @return void */ - public function addExtender(string $extender): void + public function addExtender(string $name, string $extender): void { - /** @var \Narrowspark\Automatic\Common\Contract\ScriptExtender $extender */ - $this->extenders[$extender::getType()] = new $extender($this->composer, $this->io, $this->options); + if (isset($this->extenders[$name])) { + throw new InvalidArgumentException(\sprintf('Script executor extender with the name [%s] already exists.', $name)); + } + + if (! \is_subclass_of($extender, ScriptExtenderContract::class)) { + throw new InvalidArgumentException(\sprintf('The class [%s] must implement the interface [%s].', $extender, ScriptExtenderContract::class)); + } + + $this->extenders[$name] = $extender; } /** @@ -111,7 +125,10 @@ public function execute(string $type, string $cmd): void $this->io->writeError(\sprintf('Executing script [%s]', $parsedCmd), $isVerbose); - $exitCode = $this->executor->execute($this->extenders[$type]->expand($parsedCmd), $outputHandler); + /** @var \Narrowspark\Automatic\Common\Contract\ScriptExtender $extender */ + $extender = new $this->extenders[$type]($this->composer, $this->io, $this->options); + + $exitCode = $this->executor->execute($extender->expand($parsedCmd), $outputHandler); if ($isVerbose) { $this->io->writeError(\sprintf('Executed script [%s] %s', $cmd, $exitCode === 0 ? '[OK]' : '[KO]')); diff --git a/src/Automatic/SkeletonGenerator.php b/src/Automatic/SkeletonGenerator.php index 7c48ad4d..72975a54 100644 --- a/src/Automatic/SkeletonGenerator.php +++ b/src/Automatic/SkeletonGenerator.php @@ -74,9 +74,9 @@ public function __construct( * * @throws \Exception * - * @return void + * @return \Narrowspark\Automatic\SkeletonGenerator */ - public function run(): void + public function run(): self { $generators = $this->prepareGenerators(); @@ -114,6 +114,8 @@ public function run(): void $this->installationManager->run(); $generator->generate(); + + return $this; } /** @@ -123,7 +125,7 @@ public function run(): void * * @return void */ - public function remove(): void + public function selfRemove(): void { $requires = []; diff --git a/src/Common/Contract/Resettable.php b/src/Common/Contract/Resettable.php new file mode 100644 index 00000000..f2acae47 --- /dev/null +++ b/src/Common/Contract/Resettable.php @@ -0,0 +1,18 @@ +configurator->has(MockConfigurator::getName())); - $this->configurator->clear(); + $this->configurator->reset(); static::assertFalse($this->configurator->has(MockConfigurator::getName())); } diff --git a/tests/Automatic/PathClassLoaderTest.php b/tests/Automatic/ClassLoaderTest.php similarity index 90% rename from tests/Automatic/PathClassLoaderTest.php rename to tests/Automatic/ClassLoaderTest.php index 099c51f7..8d0558ec 100644 --- a/tests/Automatic/PathClassLoaderTest.php +++ b/tests/Automatic/ClassLoaderTest.php @@ -2,7 +2,7 @@ declare(strict_types=1); namespace Narrowspark\Automatic\Test; -use Narrowspark\Automatic\PathClassLoader; +use Narrowspark\Automatic\ClassLoader; use Narrowspark\Automatic\Test\Fixture\Finder\AbstractClass; use Narrowspark\Automatic\Test\Fixture\Finder\DummyClass; use Narrowspark\Automatic\Test\Fixture\Finder\DummyClassTwo; @@ -13,12 +13,12 @@ /** * @internal */ -final class PathClassLoaderTest extends TestCase +final class ClassLoaderTest extends TestCase { /** * A path class loader instance. * - * @var \Narrowspark\Automatic\PathClassLoader + * @var \Narrowspark\Automatic\ClassLoader */ private $loader; @@ -29,7 +29,7 @@ protected function setUp() { parent::setUp(); - $this->loader = new PathClassLoader(); + $this->loader = new ClassLoader(); } public function testItFindsAllClassesInDirectoryWithGivenNamespace(): void diff --git a/tests/Automatic/Installer/AbstractInstallerTest.php b/tests/Automatic/Installer/AbstractInstallerTest.php index dba486a3..306f6527 100644 --- a/tests/Automatic/Installer/AbstractInstallerTest.php +++ b/tests/Automatic/Installer/AbstractInstallerTest.php @@ -6,9 +6,9 @@ use Composer\Package\PackageInterface; use Composer\Repository\InstalledRepositoryInterface; use Narrowspark\Automatic\Automatic; +use Narrowspark\Automatic\ClassLoader; use Narrowspark\Automatic\Common\Contract\Exception\UnexpectedValueException; use Narrowspark\Automatic\Lock; -use Narrowspark\Automatic\PathClassLoader; use Narrowspark\Automatic\Test\Traits\ArrangeComposerClasses; use Narrowspark\TestingHelper\Phpunit\MockeryTestCase; @@ -95,7 +95,7 @@ protected function setUp(): void ->once() ->andReturn($this->downloadManagerMock); - $this->configuratorInstaller = new $this->installerClass($this->ioMock, $this->composerMock, $this->lockMock, new PathClassLoader()); + $this->configuratorInstaller = new $this->installerClass($this->ioMock, $this->composerMock, $this->lockMock, new ClassLoader()); $this->repositoryMock = $this->mock(InstalledRepositoryInterface::class); $this->packageMock = $this->mock(PackageInterface::class); diff --git a/tests/Automatic/SkeletonGeneratorTest.php b/tests/Automatic/SkeletonGeneratorTest.php index b645b7e2..07b98923 100644 --- a/tests/Automatic/SkeletonGeneratorTest.php +++ b/tests/Automatic/SkeletonGeneratorTest.php @@ -163,7 +163,7 @@ public function testRemove(): void $this->lockMock->shouldReceive('write') ->once(); - $this->skeletonGenerator->remove(); + $this->skeletonGenerator->selfRemove(); } /**