diff --git a/src/Console/Command/ComposerCommand.php b/src/Console/Command/ComposerCommand.php index 653df8fb..19a691d5 100644 --- a/src/Console/Command/ComposerCommand.php +++ b/src/Console/Command/ComposerCommand.php @@ -37,16 +37,14 @@ protected function configure(): void protected function execute(InputInterface $input, OutputInterface $output): int { - $extra = array_filter($this->getRawTokens($input), fn ($item) => 'composer' !== $item); - - $vendorDirectory = $this->rootDir . Composer::VENDOR_DIR; - if (!file_exists($file = $this->rootDir . '/castor.composer.json') && !file_exists($file = $this->rootDir . '/.castor/castor.composer.json')) { // Default to the root directory (so someone can do a composer init by example) $file = $this->rootDir . '/castor.composer.json'; } - $this->composer->run($file, $vendorDirectory, $extra, $output, true); + $vendorDirectory = $this->rootDir . '/' . Composer::VENDOR_DIR; + + $this->composer->run($file, $vendorDirectory, $this->getRawTokens($input), $output, $input->isInteractive()); return Command::SUCCESS; } diff --git a/src/Console/Input/GetRawTokenTrait.php b/src/Console/Input/GetRawTokenTrait.php index a839989f..ad609e7f 100644 --- a/src/Console/Input/GetRawTokenTrait.php +++ b/src/Console/Input/GetRawTokenTrait.php @@ -23,7 +23,7 @@ private function getRawTokens(InputInterface $input): array $parameters = []; $keep = false; foreach ($tokens as $value) { - if ($value === $input->getFirstArgument()) { + if (!$keep && $value === $input->getFirstArgument()) { $keep = true; continue; diff --git a/src/Import/Importer.php b/src/Import/Importer.php index e7949f1c..6cb61299 100644 --- a/src/Import/Importer.php +++ b/src/Import/Importer.php @@ -4,7 +4,7 @@ use Castor\Import\Exception\ImportError; use Castor\Import\Exception\RemoteNotAllowed; -use Castor\Import\Remote\PackageImporter; +use Castor\Import\Remote\Composer; use JoliCode\PhpOsHelper\OsHelper; use Psr\Log\LoggerInterface; use Symfony\Component\Finder\Finder; @@ -21,7 +21,7 @@ class Importer private array $imports = []; public function __construct( - private readonly PackageImporter $packageImporter, + private readonly Composer $composer, private readonly LoggerInterface $logger, ) { } @@ -39,7 +39,7 @@ public function import(string $path, ?string $file = null): void $package = mb_substr($path, mb_strlen($scheme) + 3); try { - $this->packageImporter->importFromPackage( + $this->composer->importFromPackage( $scheme, $package, $file, diff --git a/src/Import/Remote/Composer.php b/src/Import/Remote/Composer.php index ebc88ed7..53318670 100644 --- a/src/Import/Remote/Composer.php +++ b/src/Import/Remote/Composer.php @@ -4,11 +4,17 @@ use Castor\Helper\PathHelper; use Castor\Import\Exception\ComposerError; +use Castor\Import\Exception\ImportError; +use Castor\Import\Exception\InvalidImportFormat; +use Castor\Import\Exception\RemoteNotAllowed; +use Castor\Import\Mount; +use Castor\Kernel; use Composer\Console\Application as ComposerApplication; use Psr\Log\LoggerInterface; use Psr\Log\NullLogger; use Symfony\Component\Console\Helper\ProgressIndicator; use Symfony\Component\Console\Input\ArgvInput; +use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\Output; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Filesystem\Filesystem; @@ -16,18 +22,35 @@ /** @internal */ class Composer { - public const VENDOR_DIR = '/.castor/vendor/'; + public const VENDOR_DIR = '.castor/vendor'; public function __construct( - private readonly Filesystem $filesystem, + private readonly Kernel $kernel, + private readonly InputInterface $input, private readonly OutputInterface $output, + private readonly Filesystem $filesystem, private readonly LoggerInterface $logger = new NullLogger(), ) { } - public function install(string $entrypointDirectory, bool $update = false, bool $displayProgress = true): void + public function isRemoteAllowed(): bool + { + if ($_SERVER['CASTOR_NO_REMOTE'] ?? false) { + return false; + } + + // Need to look for the raw options as the input is not yet parsed + if (true !== $this->input->getParameterOption('--no-remote', true)) { + return false; + } + + return true; + } + + public function install(string $entrypointDirectory): void { - $vendorDirectory = $entrypointDirectory . self::VENDOR_DIR; + $update = true !== $this->input->getParameterOption('--update-remotes', true); + $displayProgress = 'list' !== $this->input->getFirstArgument(); if (!file_exists($file = $entrypointDirectory . '/castor.composer.json') && !file_exists($file = $entrypointDirectory . '/.castor/castor.composer.json')) { $this->logger->debug(sprintf('The castor.composer.json file does not exists in %s or %s/.castor, skipping composer install.', $entrypointDirectory, $entrypointDirectory)); @@ -35,15 +58,14 @@ public function install(string $entrypointDirectory, bool $update = false, bool return; } + $vendorDirectory = $entrypointDirectory . '/' . self::VENDOR_DIR; + if (!$update && $this->isInstalled($vendorDirectory, $file)) { return; } - if (!file_exists($vendorDirectory)) { - $this->filesystem->mkdir($vendorDirectory); - } - - file_put_contents($vendorDirectory . '.gitignore', "*\n"); + $this->filesystem->mkdir($vendorDirectory); + $this->filesystem->dumpFile($vendorDirectory . '/.gitignore', "*\n"); $progressIndicator = null; @@ -67,22 +89,66 @@ public function install(string $entrypointDirectory, bool $update = false, bool $this->writeInstalled($vendorDirectory, $file); } - public function remove(): void + public function requireAutoload(): void + { + $autoloadPath = PathHelper::getRoot() . '/' . self::VENDOR_DIR . '/autoload.php'; + + if (!file_exists($autoloadPath)) { + return; + } + + require $autoloadPath; + } + + public function importFromPackage(string $scheme, string $package, ?string $file = null): void + { + if (!$this->isRemoteAllowed()) { + throw new RemoteNotAllowed('Remote imports are disabled.'); + } + + if (!preg_match('#^(?[^/]+)/(?[^/]+)$#', $package)) { + throw new InvalidImportFormat(sprintf('The import path must be formatted like this: "%s:///".', $scheme)); + } + + if ('composer' === $scheme || 'package' === $scheme) { + if ('package' === $scheme) { + @trigger_deprecation('castor/castor', '0.16.0', 'The "package" scheme is deprecated, use "composer" instead.'); + } + + $packageDirectory = PathHelper::getRoot() . '/' . self::VENDOR_DIR . '/' . $package; + + if (!file_exists($packageDirectory)) { + throw new ImportError(sprintf('The package "%s" is not installed, make sure you required it in your castor.composer.json file.', $package)); + } + + $this->kernel->addMount(new Mount( + PathHelper::getRoot() . '/' . self::VENDOR_DIR . '/' . $package . '/' . ($file ?? ''), + allowEmptyEntrypoint: true, + allowRemotePackage: false, + )); + + return; + } + + throw new InvalidImportFormat(sprintf('The import scheme "%s" is not supported.', $scheme)); + } + + public function clean(): void { - $this->filesystem->remove(PathHelper::getRoot() . self::VENDOR_DIR); + $this->filesystem->remove(PathHelper::getRoot() . '/' . self::VENDOR_DIR); } /** * @param string[] $args */ - public function run(string $composerJsonFilePath, string $vendorDirectory, array $args, callable|OutputInterface $callback, bool $allowInteraction = false): void + public function run(string $composerJsonFilePath, string $vendorDirectory, array $args, callable|OutputInterface $callback, bool $interactive = false): void { $this->filesystem->mkdir($vendorDirectory); $args[] = '--working-dir'; $args[] = \dirname($vendorDirectory); - if (!$allowInteraction) { + if (!$interactive) { $args[] = '--no-interaction'; } diff --git a/src/Import/Remote/PackageImporter.php b/src/Import/Remote/PackageImporter.php deleted file mode 100644 index 657435cf..00000000 --- a/src/Import/Remote/PackageImporter.php +++ /dev/null @@ -1,86 +0,0 @@ -composer->install($mount->path, $update, $displayProgress); - } - - public function importFromPackage(string $scheme, string $package, ?string $file = null): void - { - if (!$this->allowsRemote()) { - throw new RemoteNotAllowed('Remote imports are disabled.'); - } - - if (!preg_match('#^(?[^/]+)/(?[^/]+)$#', $package)) { - throw new InvalidImportFormat(sprintf('The import path must be formatted like this: "%s:///".', $scheme)); - } - - if ('composer' === $scheme || 'package' === $scheme) { - if ('package' === $scheme) { - @trigger_deprecation('castor/castor', '0.16.0', 'The "package" scheme is deprecated, use "composer" instead.'); - } - - $packageDirectory = PathHelper::getRoot() . Composer::VENDOR_DIR . $package; - - if (!file_exists($packageDirectory)) { - throw new ImportError(sprintf('The package "%s" is not installed, make sure you required it in your castor.composer.json file.', $package)); - } - - $this->kernel->addMount(new Mount( - PathHelper::getRoot() . Composer::VENDOR_DIR . $package . '/' . ($file ?? ''), - allowEmptyEntrypoint: true, - allowRemotePackage: false, - )); - - return; - } - - throw new InvalidImportFormat(sprintf('The import scheme "%s" is not supported.', $scheme)); - } - - public function clean(): void - { - $this->composer->remove(); - } - - public function allowsRemote(): bool - { - if ($_SERVER['CASTOR_NO_REMOTE'] ?? false) { - return false; - } - - // Need to look for the raw options as the input is not yet parsed - return true === $this->input->getParameterOption('--no-remote', true); - } -} diff --git a/src/Kernel.php b/src/Kernel.php index 05aec6ed..aacc108d 100644 --- a/src/Kernel.php +++ b/src/Kernel.php @@ -15,7 +15,7 @@ use Castor\Helper\PlatformHelper; use Castor\Import\Importer; use Castor\Import\Mount; -use Castor\Import\Remote\PackageImporter; +use Castor\Import\Remote\Composer; use Symfony\Component\Console\Exception\ExceptionInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -40,7 +40,7 @@ public function __construct( #[Autowire(lazy: true)] private readonly Importer $importer, #[Autowire(lazy: true)] - private readonly PackageImporter $packageImporter, + private readonly Composer $composer, private readonly FunctionResolver $functionResolver, private readonly FunctionLoader $functionLoader, private readonly ContextRegistry $contextRegistry, @@ -49,17 +49,9 @@ public function __construct( public function boot(InputInterface $input, OutputInterface $output): void { - $this->packageImporter->requireAutoload(); - $this->eventDispatcher->dispatch(new BeforeBootEvent($this->application)); - $allowRemotePackage = true; - - if ($_SERVER['CASTOR_NO_REMOTE'] ?? false) { - $allowRemotePackage = false; - } elseif (true !== $input->getParameterOption('--no-remote', true)) { - $allowRemotePackage = false; - } + $allowRemotePackage = $this->composer->isRemoteAllowed(); $this->addMount(new Mount($this->rootDir, allowRemotePackage: $allowRemotePackage)); @@ -69,6 +61,8 @@ public function boot(InputInterface $input, OutputInterface $output): void $this->load($mount, $currentFunctions, $currentClasses, $input, $output); } + + $this->composer->requireAutoload(); } public function addMount(Mount $mount): void @@ -88,10 +82,7 @@ private function load( OutputInterface $output ): void { if ($mount->allowRemotePackage) { - $update = true !== $input->getParameterOption('--update-remotes', true); - $displayProgress = 'list' !== $input->getFirstArgument(); - - $this->packageImporter->install($mount, $update, $displayProgress); + $this->composer->install($mount->path); } try { diff --git a/tests/Generated/ImportComposerNotExistingTest.php.err.txt b/tests/Generated/ImportComposerNotExistingTest.php.err.txt index be4b26f2..523ae16c 100644 --- a/tests/Generated/ImportComposerNotExistingTest.php.err.txt +++ b/tests/Generated/ImportComposerNotExistingTest.php.err.txt @@ -3,7 +3,7 @@ In castor.php line 5: Could not import "foo/bar" in ".../tests/fixtures/broken/import-composer-not-existing/castor.php" on line 5. Reason: The package "foo/bar" is not installed, make sure you required it in your castor.composer.json file. -In PackageImporter.php line XXXX: +In Composer.php line XXXX: The package "foo/bar" is not installed, make sure you required it in your castor.composer.json file. diff --git a/tests/Generated/ImportInvalidFormatTest.php.err.txt b/tests/Generated/ImportInvalidFormatTest.php.err.txt index 7525101a..59a4909e 100644 --- a/tests/Generated/ImportInvalidFormatTest.php.err.txt +++ b/tests/Generated/ImportInvalidFormatTest.php.err.txt @@ -3,7 +3,7 @@ In castor.php line 5: Could not import "invalid-package-name" in ".../tests/fixtures/broken/import-invalid-format/castor.php" on line 5. Reason: The import path must be formatted like this: "composer:///". -In PackageImporter.php line XXXX: +In Composer.php line XXXX: The import path must be formatted like this: "composer:///". diff --git a/tests/Generated/ImportInvalidPackageTest.php.err.txt b/tests/Generated/ImportInvalidPackageTest.php.err.txt index e378ba90..81e308e7 100644 --- a/tests/Generated/ImportInvalidPackageTest.php.err.txt +++ b/tests/Generated/ImportInvalidPackageTest.php.err.txt @@ -3,7 +3,7 @@ In castor.php line 5: Could not import "foo/bar" in ".../tests/fixtures/broken/import-invalid-package/castor.php" on line 5. Reason: The package "foo/bar" is not installed, make sure you required it in your castor.composer.json file. -In PackageImporter.php line XXXX: +In Composer.php line XXXX: The package "foo/bar" is not installed, make sure you required it in your castor.composer.json file.