From 088acd777e75d52d1d71f4dc977834210aa4d364 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Wed, 15 Oct 2025 20:27:21 +0100 Subject: [PATCH 1/2] Replace output throughout all of PIE witih Composer ConsoleIO --- phpstan-baseline.neon | 2 +- src/Building/Build.php | 4 +- src/Building/UnixBuild.php | 79 ++++++++------- src/Building/WindowsBuild.php | 6 +- src/Command/BuildCommand.php | 20 ++-- src/Command/CommandHelper.php | 54 +++++------ src/Command/DownloadCommand.php | 18 ++-- src/Command/InfoCommand.php | 46 ++++----- src/Command/InstallCommand.php | 23 ++--- .../InstallExtensionsForProjectCommand.php | 96 ++++++++----------- src/Command/InvokeSubCommand.php | 7 +- src/Command/RepositoryAddCommand.php | 9 +- src/Command/RepositoryListCommand.php | 9 +- src/Command/RepositoryRemoveCommand.php | 9 +- src/Command/SelfUpdateCommand.php | 45 +++++---- src/Command/SelfVerifyCommand.php | 10 +- src/Command/ShowCommand.php | 43 +++++---- src/Command/UninstallCommand.php | 14 +-- .../InstallAndBuildProcess.php | 8 +- .../OverrideDownloadUrlInstallListener.php | 2 +- .../RemoveUnrelatedInstallOperations.php | 10 +- .../PieComposerRequest.php | 6 +- .../PiePackageInstaller.php | 17 ++-- src/ComposerIntegration/UninstallProcess.php | 22 ++--- src/ComposerIntegration/VendorCleanup.php | 16 ++-- src/Container.php | 46 ++++++++- .../ResolveDependencyWithComposer.php | 10 +- .../Ini/AddExtensionToTheIniFile.php | 24 ++--- .../Ini/CheckAndAddExtensionToIniIfNeeded.php | 18 ++-- src/Installing/Ini/DockerPhpExtEnable.php | 14 +-- src/Installing/Ini/OndrejPhpenmod.php | 41 ++++---- .../Ini/PickBestSetupIniApproach.php | 20 ++-- .../Ini/PreCheckExtensionAlreadyLoaded.php | 6 +- src/Installing/Ini/RemoveIniEntry.php | 4 +- .../Ini/RemoveIniEntryWithFileGetContents.php | 8 +- src/Installing/Ini/SetupIniApproach.php | 4 +- .../Ini/StandardAdditionalPhpIniDirectory.php | 18 ++-- src/Installing/Ini/StandardSinglePhpIni.php | 6 +- src/Installing/Install.php | 4 +- .../ComposerFactoryForProject.php | 17 +--- .../InstallPiePackageFromPath.php | 19 ++-- .../InstallSelectedPackage.php | 13 ++- src/Installing/SetupIniFile.php | 14 +-- src/Installing/UnixInstall.php | 14 ++- src/Installing/WindowsInstall.php | 14 +-- src/Platform/TargetPhp/PhpBinaryPath.php | 24 ++--- .../FallbackVerificationUsingOpenSsl.php | 10 +- .../GithubCliAttestationVerification.php | 11 ++- src/SelfManage/Verify/VerifyPiePhar.php | 4 +- .../VerifyPieReleaseUsingAttestation.php | 10 +- test/integration/Building/UnixBuildTest.php | 22 ++--- test/integration/Command/BuildCommandTest.php | 3 +- test/integration/Command/CommandTester.php | 39 ++++++++ .../Command/DownloadCommandTest.php | 3 +- test/integration/Command/InfoCommandTest.php | 3 +- .../Command/InstallCommandTest.php | 3 +- ...InstallExtensionsForProjectCommandTest.php | 8 +- .../RepositoryManagementCommandsTest.php | 7 +- test/integration/Command/ShowCommandTest.php | 9 +- .../ResolveDependencyWithComposerTest.php | 4 +- .../Installing/UnixInstallTest.php | 6 +- .../Installing/WindowsInstallTest.php | 6 +- test/unit/Command/CommandHelperTest.php | 22 ++--- .../InstallAndBuildProcessTest.php | 11 +-- .../InstalledJsonMetadataTest.php | 6 +- ...OverrideDownloadUrlInstallListenerTest.php | 15 ++- .../RemoveUnrelatedInstallOperationsTest.php | 5 +- .../ComposerIntegration/VendorCleanupTest.php | 15 +-- .../ResolveDependencyWithComposerTest.php | 18 ++-- .../Ini/AddExtensionToTheIniFileTest.php | 29 +++--- .../CheckAndAddExtensionToIniIfNeededTest.php | 44 ++++----- .../Installing/Ini/DockerPhpExtEnableTest.php | 19 ++-- .../Installing/Ini/OndrejPhpenmodTest.php | 47 ++++----- .../Ini/PickBestSetupIniApproachTest.php | 27 +++--- .../PreCheckExtensionAlreadyLoadedTest.php | 15 +-- .../RemoveIniEntryWithFileGetContentsTest.php | 8 +- .../StandardAdditionalPhpIniDirectoryTest.php | 25 ++--- .../Ini/StandardSinglePhpIniTest.php | 17 ++-- .../InstallPiePackageFromPathTest.php | 12 +-- .../InstallSelectedPackageTest.php | 13 ++- .../Platform/TargetPhp/PhpBinaryPathTest.php | 15 +-- .../FallbackVerificationUsingOpenSslTest.php | 16 ++-- .../GithubCliAttestationVerificationTest.php | 14 +-- 83 files changed, 775 insertions(+), 679 deletions(-) create mode 100644 test/integration/Command/CommandTester.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index a9d7c75c..48afd727 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -21,7 +21,7 @@ parameters: - message: '#^Cannot cast mixed to string\.$#' identifier: cast.string - count: 2 + count: 1 path: src/Command/InstallExtensionsForProjectCommand.php - diff --git a/src/Building/Build.php b/src/Building/Build.php index 4ec00668..f488b149 100644 --- a/src/Building/Build.php +++ b/src/Building/Build.php @@ -4,11 +4,11 @@ namespace Php\Pie\Building; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPhp\PhpizePath; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ interface Build @@ -18,7 +18,7 @@ public function __invoke( DownloadedPackage $downloadedPackage, TargetPlatform $targetPlatform, array $configureOptions, - OutputInterface $output, + IOInterface $io, PhpizePath|null $phpizePath, ): BinaryFile; } diff --git a/src/Building/UnixBuild.php b/src/Building/UnixBuild.php index 8de37d9f..031582d2 100644 --- a/src/Building/UnixBuild.php +++ b/src/Building/UnixBuild.php @@ -4,6 +4,7 @@ namespace Php\Pie\Building; +use Composer\IO\IOInterface; use Php\Pie\ComposerIntegration\BundledPhpExtensionsRepository; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; @@ -11,7 +12,6 @@ use Php\Pie\Platform\TargetPlatform; use Php\Pie\Util\Process; use Php\Pie\Util\ProcessFailedWithLimitedOutput; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\Process as SymfonyProcess; @@ -35,13 +35,13 @@ public function __invoke( DownloadedPackage $downloadedPackage, TargetPlatform $targetPlatform, array $configureOptions, - OutputInterface $output, + IOInterface $io, PhpizePath|null $phpizePath, ): BinaryFile { $outputCallback = null; - if ($output->isVerbose()) { - $outputCallback = static function (string $type, string $outputMessage) use ($output): void { - $output->write(sprintf( + if ($io->isVerbose()) { + $outputCallback = static function (string $type, string $outputMessage) use ($io): void { + $io->write(sprintf( '%s%s%s', $type === SymfonyProcess::ERR ? '' : '', $outputMessage, @@ -58,29 +58,29 @@ public function __invoke( * already clean anyway; however, sometimes we want to rebuild the * current ext, so this will perform a clean first */ - $this->cleanup($phpizePath, $downloadedPackage, $output, $outputCallback); + $this->cleanup($phpizePath, $downloadedPackage, $io, $outputCallback); $this->phpize( $phpizePath, $downloadedPackage, - $output, + $io, $outputCallback, ); - $output->writeln('phpize complete.'); + $io->write('phpize complete.'); $phpConfigPath = $targetPlatform->phpBinaryPath->phpConfigPath(); if ($phpConfigPath !== null) { $configureOptions[] = '--with-php-config=' . $phpConfigPath; } - $this->configure($downloadedPackage, $configureOptions, $output, $outputCallback); + $this->configure($downloadedPackage, $configureOptions, $io, $outputCallback); $optionsOutput = count($configureOptions) ? ' with options: ' . implode(' ', $configureOptions) : '.'; - $output->writeln('Configure complete' . $optionsOutput); + $io->write('Configure complete' . $optionsOutput); try { - $this->make($targetPlatform, $downloadedPackage, $output, $outputCallback); + $this->make($targetPlatform, $downloadedPackage, $io, $outputCallback); } catch (ProcessFailedException $p) { throw ProcessFailedWithLimitedOutput::fromProcessFailedException($p); } @@ -91,7 +91,7 @@ public function __invoke( throw ExtensionBinaryNotFound::fromExpectedBinary($expectedSoFile); } - $output->writeln(sprintf( + $io->write(sprintf( 'Build complete: %s', $expectedSoFile, )); @@ -99,18 +99,18 @@ public function __invoke( return BinaryFile::fromFileWithSha256Checksum($expectedSoFile); } - private function renamesToConfigM4(DownloadedPackage $downloadedPackage, OutputInterface $output): void + private function renamesToConfigM4(DownloadedPackage $downloadedPackage, IOInterface $io): void { $configM4 = $downloadedPackage->extractedSourcePath . DIRECTORY_SEPARATOR . 'config.m4'; if (file_exists($configM4)) { return; } - $output->writeln('config.m4 does not exist; checking alternatives', OutputInterface::VERBOSITY_VERY_VERBOSE); + $io->write('config.m4 does not exist; checking alternatives', verbosity: IOInterface::VERY_VERBOSE); foreach (['config0.m4', 'config9.m4'] as $alternateConfigM4) { $fullPathToAlternate = $downloadedPackage->extractedSourcePath . DIRECTORY_SEPARATOR . $alternateConfigM4; if (file_exists($fullPathToAlternate)) { - $output->writeln(sprintf('Renaming %s to config.m4', $alternateConfigM4), OutputInterface::VERBOSITY_VERY_VERBOSE); + $io->write(sprintf('Renaming %s to config.m4', $alternateConfigM4), verbosity: IOInterface::VERY_VERBOSE); rename($fullPathToAlternate, $configM4); return; @@ -122,16 +122,17 @@ private function renamesToConfigM4(DownloadedPackage $downloadedPackage, OutputI private function phpize( PhpizePath $phpize, DownloadedPackage $downloadedPackage, - OutputInterface $output, + IOInterface $io, callable|null $outputCallback, ): void { $phpizeCommand = [$phpize->phpizeBinaryPath]; - if ($output->isVerbose()) { - $output->writeln('Running phpize step using: ' . implode(' ', $phpizeCommand) . ''); - } + $io->write( + 'Running phpize step using: ' . implode(' ', $phpizeCommand) . '', + verbosity: IOInterface::VERBOSE, + ); - $this->renamesToConfigM4($downloadedPackage, $output); + $this->renamesToConfigM4($downloadedPackage, $io); Process::run( $phpizeCommand, @@ -148,14 +149,15 @@ private function phpize( private function configure( DownloadedPackage $downloadedPackage, array $configureOptions, - OutputInterface $output, + IOInterface $io, callable|null $outputCallback, ): void { $configureCommand = ['./configure', ...$configureOptions]; - if ($output->isVerbose()) { - $output->writeln('Running configure step with: ' . implode(' ', $configureCommand) . ''); - } + $io->write( + 'Running configure step with: ' . implode(' ', $configureCommand) . '', + verbosity: IOInterface::VERBOSE, + ); Process::run( $configureCommand, @@ -169,13 +171,13 @@ private function configure( private function make( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, - OutputInterface $output, + IOInterface $io, callable|null $outputCallback, ): void { $makeCommand = ['make']; if ($targetPlatform->makeParallelJobs === 1) { - $output->writeln('Running make without parallelization - try providing -jN to PIE where N is the number of cores you have.'); + $io->write('Running make without parallelization - try providing -jN to PIE where N is the number of cores you have.'); } else { $makeCommand[] = sprintf('-j%d', $targetPlatform->makeParallelJobs); } @@ -185,9 +187,10 @@ private function make( $downloadedPackage, ); - if ($output->isVerbose()) { - $output->writeln('Running make step with: ' . implode(' ', $makeCommand) . ''); - } + $io->write( + 'Running make step with: ' . implode(' ', $makeCommand) . '', + verbosity: IOInterface::VERBOSE, + ); Process::run( $makeCommand, @@ -201,7 +204,7 @@ private function make( private function cleanup( PhpizePath $phpize, DownloadedPackage $downloadedPackage, - OutputInterface $output, + IOInterface $io, callable|null $outputCallback, ): void { /** @@ -210,18 +213,20 @@ private function cleanup( * configure script manually... */ if (! file_exists($downloadedPackage->extractedSourcePath . '/configure')) { - if ($output->isVerbose()) { - $output->writeln('Skipping phpize --clean, configure does not exist'); - } + $io->write( + 'Skipping phpize --clean, configure does not exist', + verbosity: IOInterface::VERBOSE, + ); return; } $phpizeCleanCommand = [$phpize->phpizeBinaryPath, '--clean']; - if ($output->isVerbose()) { - $output->writeln('Running phpize --clean step using: ' . implode(' ', $phpizeCleanCommand) . ''); - } + $io->write( + 'Running phpize --clean step using: ' . implode(' ', $phpizeCleanCommand) . '', + verbosity: IOInterface::VERBOSE, + ); Process::run( $phpizeCleanCommand, @@ -230,6 +235,6 @@ private function cleanup( $outputCallback, ); - $output->writeln('Build files cleaned up.'); + $io->write('Build files cleaned up.'); } } diff --git a/src/Building/WindowsBuild.php b/src/Building/WindowsBuild.php index 66cbff49..39bb2a1b 100644 --- a/src/Building/WindowsBuild.php +++ b/src/Building/WindowsBuild.php @@ -4,12 +4,12 @@ namespace Php\Pie\Building; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPhp\PhpizePath; use Php\Pie\Platform\TargetPlatform; use Php\Pie\Platform\WindowsExtensionAssetName; -use Symfony\Component\Console\Output\OutputInterface; use function sprintf; @@ -21,12 +21,12 @@ public function __invoke( DownloadedPackage $downloadedPackage, TargetPlatform $targetPlatform, array $configureOptions, - OutputInterface $output, + IOInterface $io, PhpizePath|null $phpizePath, ): BinaryFile { $prebuiltDll = WindowsExtensionAssetName::determineDllName($targetPlatform, $downloadedPackage); - $output->writeln(sprintf( + $io->write(sprintf( 'Nothing to do on Windows, prebuilt DLL found: %s', $prebuiltDll, )); diff --git a/src/Command/BuildCommand.php b/src/Command/BuildCommand.php index 0fe3deb2..38027290 100644 --- a/src/Command/BuildCommand.php +++ b/src/Command/BuildCommand.php @@ -4,6 +4,7 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; use Php\Pie\ComposerIntegration\ComposerIntegrationHandler; use Php\Pie\ComposerIntegration\ComposerRunFailed; use Php\Pie\ComposerIntegration\PieComposerFactory; @@ -33,6 +34,7 @@ public function __construct( private readonly DependencyResolver $dependencyResolver, private readonly ComposerIntegrationHandler $composerIntegrationHandler, private readonly FindMatchingPackages $findMatchingPackages, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -46,14 +48,14 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); try { $requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input); } catch (InvalidPackageName $invalidPackageName) { return CommandHelper::handlePackageNotFound( $invalidPackageName, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); @@ -64,7 +66,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $composer = PieComposerFactory::createPieComposer( $this->container, new PieComposerRequest( - $output, + $this->io, $targetPlatform, $requestedNameAndVersion, PieOperation::Resolve, @@ -85,18 +87,18 @@ public function execute(InputInterface $input, OutputInterface $output): int return CommandHelper::handlePackageNotFound( $unableToResolveRequirement, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $output->writeln(''); - $output->writeln('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->write(''); + $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } - $output->writeln(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); + $this->io->write(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); // Now we know what package we have, we can validate the configure options for the command and re-create the // Composer instance with the populated configure options @@ -106,7 +108,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $composer = PieComposerFactory::createPieComposer( $this->container, new PieComposerRequest( - $output, + $this->io, $targetPlatform, $requestedNameAndVersion, PieOperation::Build, @@ -126,7 +128,7 @@ public function execute(InputInterface $input, OutputInterface $output): int false, ); } catch (ComposerRunFailed $composerRunFailed) { - $output->writeln('' . $composerRunFailed->getMessage() . ''); + $this->io->write('' . $composerRunFailed->getMessage() . ''); return $composerRunFailed->getCode(); } diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index 086e5ba1..2a17be7f 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -5,6 +5,7 @@ namespace Php\Pie\Command; use Composer\Composer; +use Composer\IO\IOInterface; use Composer\Package\CompletePackageInterface; use Composer\Package\Version\VersionParser; use Composer\Repository\ComposerRepository; @@ -30,7 +31,6 @@ use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; use Throwable; use Webmozart\Assert\Assert; @@ -144,7 +144,7 @@ public static function validateInput(InputInterface $input, Command $command): v $input->bind($command->getDefinition()); } - public static function determineTargetPlatformFromInputs(InputInterface $input, OutputInterface $output): TargetPlatform + public static function determineTargetPlatformFromInputs(InputInterface $input, IOInterface $io): TargetPlatform { $phpBinaryPath = PhpBinaryPath::fromCurrentProcess(); @@ -190,8 +190,8 @@ public static function determineTargetPlatformFromInputs(InputInterface $input, $targetPlatform = TargetPlatform::fromPhpBinaryPath($phpBinaryPath, $makeParallelJobs); - $output->writeln(sprintf('You are running PHP %s', PHP_VERSION)); - $output->writeln(sprintf( + $io->write(sprintf('You are running PHP %s', PHP_VERSION)); + $io->write(sprintf( 'Target PHP installation: %s %s%s, on %s %s (from %s)', $phpBinaryPath->version(), $targetPlatform->threadSafety->asShort(), @@ -200,12 +200,12 @@ public static function determineTargetPlatformFromInputs(InputInterface $input, $targetPlatform->architecture->name, $phpBinaryPath->phpBinaryPath, )); - $output->writeln( + $io->write( sprintf( 'Using pie.json: %s', PiePlatform::getPieJsonFilename($targetPlatform), ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return $targetPlatform; @@ -306,9 +306,9 @@ public static function processConfigureOptionsFromInput(Package $package, InputI return $configureOptionsValues; } - public static function listRepositories(Composer $composer, OutputInterface $output): void + public static function listRepositories(Composer $composer, IOInterface $io): void { - $output->writeln('The following repositories are in use for this Target PHP:'); + $io->write('The following repositories are in use for this Target PHP:'); foreach ($composer->getRepositoryManager()->getRepositories() as $repo) { if ($repo instanceof ComposerRepository) { @@ -317,16 +317,16 @@ public static function listRepositories(Composer $composer, OutputInterface $out $repoUrl = array_key_exists('url', $repoConfig) && is_string($repoConfig['url']) && $repoConfig['url'] !== '' ? $repoConfig['url'] : null; if ($repoUrl === 'https://repo.packagist.org') { - $output->writeln(' - Packagist'); + $io->write(' - Packagist'); continue; } - $output->writeln(sprintf(' - Composer (%s)', $repoUrl ?? 'no url?')); + $io->write(sprintf(' - Composer (%s)', $repoUrl ?? 'no url?')); continue; } if ($repo instanceof VcsRepository) { - $output->writeln(sprintf( + $io->write(sprintf( ' - VCS Repository (%s)', $repo->getDriver()?->getUrl() ?? 'no url?', )); @@ -338,7 +338,7 @@ public static function listRepositories(Composer $composer, OutputInterface $out } $repoConfig = $repo->getRepoConfig(); - $output->writeln(sprintf( + $io->write(sprintf( ' - Path Repository (%s)', array_key_exists('url', $repoConfig) && is_string($repoConfig['url']) && $repoConfig['url'] !== '' ? $repoConfig['url'] : 'no path?', )); @@ -348,14 +348,14 @@ public static function listRepositories(Composer $composer, OutputInterface $out public static function handlePackageNotFound( InvalidPackageName|UnableToResolveRequirement $exception, FindMatchingPackages $findMatchingPackages, - OutputInterface $output, + IOInterface $io, TargetPlatform $targetPlatform, ContainerInterface $container, ): int { $pieComposer = PieComposerFactory::createPieComposer( $container, PieComposerRequest::noOperation( - $output, + $io, $targetPlatform, ), ); @@ -365,13 +365,13 @@ public static function handlePackageNotFound( $requestedPackageName = substr($requestedPackageName, 4); } - $output->writeln(''); - $output->writeln(sprintf('Could not install package: %s', $requestedPackageName)); - $output->writeln($exception->getMessage()); + $io->write(''); + $io->write(sprintf('Could not install package: %s', $requestedPackageName)); + $io->write($exception->getMessage()); try { $matches = array_map( - static function (array $match) use ($output, $pieComposer): array { + static function (array $match) use ($io, $pieComposer): array { $composerMatchingPackage = $pieComposer->getRepositoryManager()->findPackage($match['name'], '*'); // Attempts to augment the Composer packages found with the PIE extension name @@ -382,13 +382,13 @@ static function (array $match) use ($output, $pieComposer): array { ->extensionName() ->name(); } catch (Throwable $t) { - $output->writeln( + $io->write( sprintf( 'Tried looking up extension name for %s, but failed: %s', $match['name'], $t->getMessage(), ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); } } @@ -399,16 +399,16 @@ static function (array $match) use ($output, $pieComposer): array { ); if (count($matches)) { - $output->writeln(''); + $io->write(''); if (count($matches) === 1) { - $output->writeln('Did you mean this?'); + $io->write('Did you mean this?'); } else { - $output->writeln('Did you mean one of these?'); + $io->write('Did you mean one of these?'); } array_map( - static function (array $match) use ($output): void { - $output->writeln(sprintf( + static function (array $match) use ($io): void { + $io->write(sprintf( ' - %s%s: %s', $match['name'], array_key_exists('extension-name', $match) && is_string($match['extension-name']) @@ -421,12 +421,12 @@ static function (array $match) use ($output): void { ); } } catch (OutOfRangeException) { - $output->writeln( + $io->write( sprintf( 'Tried searching for "%s", but nothing was found.', $requestedPackageName, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); } diff --git a/src/Command/DownloadCommand.php b/src/Command/DownloadCommand.php index 64d0f074..b921fb98 100644 --- a/src/Command/DownloadCommand.php +++ b/src/Command/DownloadCommand.php @@ -4,6 +4,7 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; use Php\Pie\ComposerIntegration\ComposerIntegrationHandler; use Php\Pie\ComposerIntegration\ComposerRunFailed; use Php\Pie\ComposerIntegration\PieComposerFactory; @@ -33,6 +34,7 @@ public function __construct( private readonly DependencyResolver $dependencyResolver, private readonly ComposerIntegrationHandler $composerIntegrationHandler, private readonly FindMatchingPackages $findMatchingPackages, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -48,14 +50,14 @@ public function execute(InputInterface $input, OutputInterface $output): int { CommandHelper::validateInput($input, $this); - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); try { $requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input); } catch (InvalidPackageName $invalidPackageName) { return CommandHelper::handlePackageNotFound( $invalidPackageName, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); @@ -66,7 +68,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $composer = PieComposerFactory::createPieComposer( $this->container, new PieComposerRequest( - $output, + $this->io, $targetPlatform, $requestedNameAndVersion, PieOperation::Download, @@ -87,18 +89,18 @@ public function execute(InputInterface $input, OutputInterface $output): int return CommandHelper::handlePackageNotFound( $unableToResolveRequirement, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $output->writeln(''); - $output->writeln('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->write(''); + $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } - $output->writeln(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); + $this->io->write(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); try { $this->composerIntegrationHandler->runInstall( @@ -110,7 +112,7 @@ public function execute(InputInterface $input, OutputInterface $output): int false, ); } catch (ComposerRunFailed $composerRunFailed) { - $output->writeln('' . $composerRunFailed->getMessage() . ''); + $this->io->write('' . $composerRunFailed->getMessage() . ''); return $composerRunFailed->getCode(); } diff --git a/src/Command/InfoCommand.php b/src/Command/InfoCommand.php index 755c5bc6..554d43a3 100644 --- a/src/Command/InfoCommand.php +++ b/src/Command/InfoCommand.php @@ -4,6 +4,7 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; use Composer\Semver\Constraint\Constraint; use Php\Pie\ComposerIntegration\PhpBinaryPathBasedPlatformRepository; use Php\Pie\ComposerIntegration\PieComposerFactory; @@ -38,6 +39,7 @@ public function __construct( private readonly ContainerInterface $container, private readonly DependencyResolver $dependencyResolver, private readonly FindMatchingPackages $findMatchingPackages, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -53,7 +55,7 @@ public function execute(InputInterface $input, OutputInterface $output): int { CommandHelper::validateInput($input, $this); - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); try { $requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input); @@ -61,7 +63,7 @@ public function execute(InputInterface $input, OutputInterface $output): int return CommandHelper::handlePackageNotFound( $invalidPackageName, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); @@ -70,7 +72,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $composer = PieComposerFactory::createPieComposer( $this->container, new PieComposerRequest( - $output, + $this->io, $targetPlatform, $requestedNameAndVersion, PieOperation::Resolve, @@ -91,31 +93,31 @@ public function execute(InputInterface $input, OutputInterface $output): int return CommandHelper::handlePackageNotFound( $unableToResolveRequirement, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $output->writeln(''); - $output->writeln('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->write(''); + $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } - $output->writeln(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); + $this->io->write(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); - $output->writeln(sprintf('Extension name: %s', $package->extensionName()->name())); - $output->writeln(sprintf('Extension type: %s (%s)', $package->extensionType()->value, $package->extensionType()->name)); - $output->writeln(sprintf('Composer package name: %s', $package->name())); - $output->writeln(sprintf('Version: %s', $package->version())); - $output->writeln(sprintf('Download URL: %s', $package->downloadUrl() ?? '(not specified)')); - $output->writeln(sprintf( + $this->io->write(sprintf('Extension name: %s', $package->extensionName()->name())); + $this->io->write(sprintf('Extension type: %s (%s)', $package->extensionType()->value, $package->extensionType()->name)); + $this->io->write(sprintf('Composer package name: %s', $package->name())); + $this->io->write(sprintf('Version: %s', $package->version())); + $this->io->write(sprintf('Download URL: %s', $package->downloadUrl() ?? '(not specified)')); + $this->io->write(sprintf( 'TS/NTS: %s', ($targetPlatform->threadSafety === ThreadSafetyMode::NonThreadSafe && ! $package->supportNts()) || ($targetPlatform->threadSafety === ThreadSafetyMode::ThreadSafe && ! $package->supportZts()) ? sprintf('%s (not supported on %s)', Emoji::PROHIBITED, $targetPlatform->threadSafety->asShort()) : Emoji::GREEN_CHECKMARK, )); - $output->writeln(sprintf( + $this->io->write(sprintf( 'OS: %s', ($package->compatibleOsFamilies() === null || in_array($targetPlatform->operatingSystemFamily, $package->compatibleOsFamilies(), true)) && ($package->incompatibleOsFamilies() === null || ! in_array($targetPlatform->operatingSystemFamily, $package->incompatibleOsFamilies(), true)) @@ -123,7 +125,7 @@ public function execute(InputInterface $input, OutputInterface $output): int : sprintf('%s (not supported on %s)', Emoji::PROHIBITED, $targetPlatform->operatingSystemFamily->value), )); - $output->writeln("\nDependencies:"); + $this->io->write("\nDependencies:"); $requires = $package->composerPackage()->getRequires(); if (count($requires) > 0) { @@ -137,29 +139,29 @@ public function execute(InputInterface $input, OutputInterface $output): int foreach ($requires as $requireName => $requireLink) { $packageStatus = sprintf(' %s: %s %%s', $requireName, $requireLink->getConstraint()->getPrettyString()); if (! array_key_exists($requireName, $platformConstraints)) { - $output->writeln(sprintf($packageStatus, Emoji::PROHIBITED . ' (not installed)')); + $this->io->write(sprintf($packageStatus, Emoji::PROHIBITED . ' (not installed)')); continue; } foreach ($platformConstraints[$requireName] as $constraint) { if ($requireLink->getConstraint()->matches($constraint)) { - $output->writeln(sprintf($packageStatus, Emoji::GREEN_CHECKMARK)); + $this->io->write(sprintf($packageStatus, Emoji::GREEN_CHECKMARK)); } else { - $output->writeln(sprintf($packageStatus, Emoji::PROHIBITED . ' (your version is ' . $constraint->getVersion() . ')')); + $this->io->write(sprintf($packageStatus, Emoji::PROHIBITED . ' (your version is ' . $constraint->getVersion() . ')')); } } } } else { - $output->writeln(' No dependencies.'); + $this->io->write(' No dependencies.'); } - $output->writeln("\nConfigure options:"); + $this->io->write("\nConfigure options:"); if (count($package->configureOptions())) { foreach ($package->configureOptions() as $configureOption) { - $output->writeln(sprintf(' --%s%s (%s)', $configureOption->name, $configureOption->needsValue ? '=?' : '', $configureOption->description)); + $this->io->write(sprintf(' --%s%s (%s)', $configureOption->name, $configureOption->needsValue ? '=?' : '', $configureOption->description)); } } else { - $output->writeln(' No configure options are specified.'); + $this->io->write(' No configure options are specified.'); } return Command::SUCCESS; diff --git a/src/Command/InstallCommand.php b/src/Command/InstallCommand.php index b78632ed..d3d98f3e 100644 --- a/src/Command/InstallCommand.php +++ b/src/Command/InstallCommand.php @@ -4,6 +4,7 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; use Php\Pie\ComposerIntegration\ComposerIntegrationHandler; use Php\Pie\ComposerIntegration\ComposerRunFailed; use Php\Pie\ComposerIntegration\PieComposerFactory; @@ -35,6 +36,7 @@ public function __construct( private readonly ComposerIntegrationHandler $composerIntegrationHandler, private readonly InvokeSubCommand $invokeSubCommand, private readonly FindMatchingPackages $findMatchingPackages, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -53,22 +55,21 @@ public function execute(InputInterface $input, OutputInterface $output): int $this, ['command' => 'install-extensions-for-project'], $input, - $output, ); } if (! TargetPlatform::isRunningAsRoot()) { - $output->writeln('This command may need elevated privileges, and may prompt you for your password.'); + $this->io->write('This command may need elevated privileges, and may prompt you for your password.'); } - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); try { $requestedNameAndVersion = CommandHelper::requestedNameAndVersionPair($input); } catch (InvalidPackageName $invalidPackageName) { return CommandHelper::handlePackageNotFound( $invalidPackageName, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); @@ -79,7 +80,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $composer = PieComposerFactory::createPieComposer( $this->container, new PieComposerRequest( - $output, + $this->io, $targetPlatform, $requestedNameAndVersion, PieOperation::Resolve, @@ -100,18 +101,18 @@ public function execute(InputInterface $input, OutputInterface $output): int return CommandHelper::handlePackageNotFound( $unableToResolveRequirement, $this->findMatchingPackages, - $output, + $this->io, $targetPlatform, $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $output->writeln(''); - $output->writeln('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->write(''); + $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } - $output->writeln(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); + $this->io->write(sprintf('Found package: %s which provides %s', $package->prettyNameAndVersion(), $package->extensionName()->nameWithExtPrefix())); // Now we know what package we have, we can validate the configure options for the command and re-create the // Composer instance with the populated configure options @@ -121,7 +122,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $composer = PieComposerFactory::createPieComposer( $this->container, new PieComposerRequest( - $output, + $this->io, $targetPlatform, $requestedNameAndVersion, PieOperation::Install, @@ -141,7 +142,7 @@ public function execute(InputInterface $input, OutputInterface $output): int true, ); } catch (ComposerRunFailed $composerRunFailed) { - $output->writeln('' . $composerRunFailed->getMessage() . ''); + $this->io->write('' . $composerRunFailed->getMessage() . ''); return $composerRunFailed->getCode(); } diff --git a/src/Command/InstallExtensionsForProjectCommand.php b/src/Command/InstallExtensionsForProjectCommand.php index 3db5add5..8da33125 100644 --- a/src/Command/InstallExtensionsForProjectCommand.php +++ b/src/Command/InstallExtensionsForProjectCommand.php @@ -4,6 +4,8 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use Composer\Package\Link; use Composer\Package\Version\VersionParser; use OutOfRangeException; @@ -23,15 +25,12 @@ use Psr\Container\ContainerInterface; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Helper\QuestionHelper; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Question\ChoiceQuestion; use Throwable; use function array_column; +use function array_key_exists; use function array_keys; use function array_map; use function array_merge; @@ -46,8 +45,6 @@ use function is_string; use function realpath; use function sprintf; -use function strpos; -use function substr; use const PHP_EOL; @@ -65,6 +62,7 @@ public function __construct( private readonly InstallSelectedPackage $installSelectedPackage, private readonly InstallPiePackageFromPath $installPiePackageFromPath, private readonly ContainerInterface $container, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -78,35 +76,32 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { - $helper = $this->getHelper('question'); - assert($helper instanceof QuestionHelper); - $workingDirOption = (string) $input->getOption(CommandHelper::OPTION_WORKING_DIRECTORY); $restoreWorkingDir = static function (): void { }; if ($workingDirOption !== '' && is_dir($workingDirOption)) { $currentWorkingDir = getcwd(); - $restoreWorkingDir = static function () use ($currentWorkingDir, $output): void { + $restoreWorkingDir = function () use ($currentWorkingDir): void { chdir($currentWorkingDir); - $output->writeln( + $this->io->write( sprintf('Restored working directory to: %s', $currentWorkingDir), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); }; chdir($workingDirOption); - $output->writeln( + $this->io->write( sprintf('Changed working directory to: %s', $workingDirOption), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); } - $rootPackage = $this->composerFactoryForProject->rootPackage($input, $output); + $rootPackage = $this->composerFactoryForProject->rootPackage($this->io); if (ExtensionType::isValid($rootPackage->getType())) { $cwd = realpath(getcwd()); if (! is_string($cwd) || $cwd === '') { - $output->writeln('Failed to determine current working directory.'); + $this->io->write('Failed to determine current working directory.'); $restoreWorkingDir(); @@ -117,9 +112,9 @@ public function execute(InputInterface $input, OutputInterface $output): int $this, $cwd, $rootPackage, - PieJsonEditor::fromTargetPlatform(CommandHelper::determineTargetPlatformFromInputs($input, new NullOutput())), + PieJsonEditor::fromTargetPlatform(CommandHelper::determineTargetPlatformFromInputs($input, new NullIO())), $input, - $output, + $this->io, ); $restoreWorkingDir(); @@ -129,7 +124,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $allowNonInteractive = $input->hasOption(CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL) && $input->getOption(CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL); if (! Platform::isInteractive() && ! $allowNonInteractive) { - $output->writeln(sprintf( + $this->io->write(sprintf( 'Aborting! You are not running in interactive mode, and --%s was not specified.', CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL, )); @@ -137,20 +132,20 @@ public function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); - $output->writeln(sprintf( + $this->io->write(sprintf( 'Checking extensions for your project %s (path: %s)', $rootPackage->getPrettyName(), getcwd(), )); - $extensionsRequired = $this->determineExtensionsRequired->forProject($this->composerFactoryForProject->composer($input, $output)); + $extensionsRequired = $this->determineExtensionsRequired->forProject($this->composerFactoryForProject->composer($this->io)); $pieComposer = PieComposerFactory::createPieComposer( $this->container, PieComposerRequest::noOperation( - new NullOutput(), + new NullIO(), $targetPlatform, ), ); @@ -162,7 +157,7 @@ public function execute(InputInterface $input, OutputInterface $output): int array_walk( $extensionsRequired, - function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePackages, $input, $output, $helper, &$anyErrorsHappened): void { + function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePackages, $input, &$anyErrorsHappened): void { $extension = ExtensionName::normaliseFromString($link->getTarget()); $linkRequiresConstraint = $link->getPrettyConstraint(); @@ -182,7 +177,7 @@ function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePac if (in_array($extension->name(), $phpEnabledExtensions)) { if ($piePackageVersion !== null && $piePackageVersionMatchesLinkConstraint === false) { - $output->writeln(sprintf( + $this->io->write(sprintf( '%s: %s:%s %s Version %s is installed, but does not meet the version requirement %s', $link->getDescription(), $link->getTarget(), @@ -195,7 +190,7 @@ function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePac return; } - $output->writeln(sprintf( + $this->io->write(sprintf( '%s: %s:%s %s Already installed', $link->getDescription(), $link->getTarget(), @@ -206,7 +201,7 @@ function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePac return; } - $output->writeln(sprintf( + $this->io->write(sprintf( '%s: %s:%s %s Missing', $link->getDescription(), $link->getTarget(), @@ -219,18 +214,10 @@ function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePac } catch (OutOfRangeException) { $anyErrorsHappened = true; - $message = sprintf( + $this->io->writeError(sprintf( 'No packages were found for %s', $extension->nameWithExtPrefix(), - ); - - if ($output instanceof ConsoleOutputInterface) { - $output->getErrorOutput()->writeln($message); - - return; - } - - $output->writeln($message); + )); return; } @@ -239,7 +226,7 @@ function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePac $anyErrorsHappened = true; // @todo Figure out if there is a way to improve this, safely - $output->writeln(sprintf( + $this->io->write(sprintf( "Multiple packages were found for %s:\n %s\n\nThis means you cannot `pie install` this project interactively for now.", $extension->nameWithExtPrefix(), implode("\n ", array_column($matches, 'name')), @@ -249,7 +236,7 @@ function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePac } if (Platform::isInteractive()) { - $choiceQuestion = new ChoiceQuestion( + $selectedPackageAnswer = (int) $this->io->select( "\nThe following packages may be suitable, which would you like to install: ", array_merge( ['None'], @@ -260,19 +247,20 @@ static function (array $match): string { $matches, ), ), - 0, + '0', ); - $selectedPackageAnswer = (string) $helper->ask($input, $output, $choiceQuestion); - - if ($selectedPackageAnswer === 'None') { - $output->writeln('Okay I won\'t install anything for ' . $extension->name()); + if ($selectedPackageAnswer === 0) { + $this->io->write('Okay I won\'t install anything for ' . $extension->name()); $anyErrorsHappened = true; return; } - $selectedPackageName = substr($selectedPackageAnswer, 0, (int) strpos($selectedPackageAnswer, ':')); + $matchesKey = $selectedPackageAnswer - 1; + assert(array_key_exists($matchesKey, $matches)); + + $selectedPackageName = $matches[$matchesKey]['name']; } else { $selectedPackageName = $matches[0]['name']; } @@ -283,32 +271,24 @@ static function (array $match): string { } try { - $output->writeln( + $this->io->write( sprintf('Invoking pie install of %s%s', $selectedPackageName, $requestInstallConstraint), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); $this->installSelectedPackage->withPieCli( $selectedPackageName . $requestInstallConstraint, $input, - $output, + $this->io, ); } catch (Throwable $t) { $anyErrorsHappened = true; - $message = '' . $t->getMessage() . ''; - - if ($output instanceof ConsoleOutputInterface) { - $output->getErrorOutput()->writeln($message); - - return; - } - - $output->writeln($message); + $this->io->writeError('' . $t->getMessage() . ''); } }, ); - $output->writeln(PHP_EOL . 'Finished checking extensions.'); + $this->io->write(PHP_EOL . 'Finished checking extensions.'); $restoreWorkingDir(); diff --git a/src/Command/InvokeSubCommand.php b/src/Command/InvokeSubCommand.php index b6b2546a..35977b1e 100644 --- a/src/Command/InvokeSubCommand.php +++ b/src/Command/InvokeSubCommand.php @@ -20,12 +20,15 @@ /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ class InvokeSubCommand { + public function __construct(private readonly OutputInterface $output) + { + } + /** @param array $subCommandInput */ public function __invoke( Command $command, array $subCommandInput, InputInterface $originalCommandInput, - OutputInterface $output, ): int { $originalSuppliedOptions = array_filter($originalCommandInput->getOptions()); $installForProjectInput = new ArrayInput(array_merge( @@ -39,6 +42,6 @@ public function __invoke( $application = $command->getApplication(); Assert::notNull($application); - return $application->doRun($installForProjectInput, $output); + return $application->doRun($installForProjectInput, $this->output); } } diff --git a/src/Command/RepositoryAddCommand.php b/src/Command/RepositoryAddCommand.php index 3deb6ae0..b1266cdb 100644 --- a/src/Command/RepositoryAddCommand.php +++ b/src/Command/RepositoryAddCommand.php @@ -4,6 +4,8 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use Php\Pie\ComposerIntegration\PieComposerFactory; use Php\Pie\ComposerIntegration\PieComposerRequest; use Php\Pie\ComposerIntegration\PieJsonEditor; @@ -31,6 +33,7 @@ final class RepositoryAddCommand extends Command public function __construct( private readonly ContainerInterface $container, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -55,7 +58,7 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); $pieJsonEditor = PieJsonEditor::fromTargetPlatform($targetPlatform); $type = (string) $input->getArgument(self::ARG_TYPE); @@ -84,11 +87,11 @@ public function execute(InputInterface $input, OutputInterface $output): int PieComposerFactory::createPieComposer( $this->container, PieComposerRequest::noOperation( - $output, + new NullIO(), $targetPlatform, ), ), - $output, + $this->io, ); return 0; diff --git a/src/Command/RepositoryListCommand.php b/src/Command/RepositoryListCommand.php index a2d6dddd..f69f1cb8 100644 --- a/src/Command/RepositoryListCommand.php +++ b/src/Command/RepositoryListCommand.php @@ -4,6 +4,8 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use Php\Pie\ComposerIntegration\PieComposerFactory; use Php\Pie\ComposerIntegration\PieComposerRequest; use Psr\Container\ContainerInterface; @@ -20,6 +22,7 @@ final class RepositoryListCommand extends Command { public function __construct( private readonly ContainerInterface $container, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -37,11 +40,11 @@ public function execute(InputInterface $input, OutputInterface $output): int PieComposerFactory::createPieComposer( $this->container, PieComposerRequest::noOperation( - $output, - CommandHelper::determineTargetPlatformFromInputs($input, $output), + new NullIO(), + CommandHelper::determineTargetPlatformFromInputs($input, $this->io), ), ), - $output, + $this->io, ); return 0; diff --git a/src/Command/RepositoryRemoveCommand.php b/src/Command/RepositoryRemoveCommand.php index d12f62fb..69e65865 100644 --- a/src/Command/RepositoryRemoveCommand.php +++ b/src/Command/RepositoryRemoveCommand.php @@ -4,6 +4,8 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use Php\Pie\ComposerIntegration\PieComposerFactory; use Php\Pie\ComposerIntegration\PieComposerRequest; use Php\Pie\ComposerIntegration\PieJsonEditor; @@ -27,6 +29,7 @@ final class RepositoryRemoveCommand extends Command public function __construct( private readonly ContainerInterface $container, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -46,7 +49,7 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); $pieJsonEditor = PieJsonEditor::fromTargetPlatform($targetPlatform); $url = (string) $input->getArgument(self::ARG_URL); @@ -67,11 +70,11 @@ public function execute(InputInterface $input, OutputInterface $output): int PieComposerFactory::createPieComposer( $this->container, PieComposerRequest::noOperation( - $output, + new NullIO(), $targetPlatform, ), ), - $output, + $this->io, ); return 0; diff --git a/src/Command/SelfUpdateCommand.php b/src/Command/SelfUpdateCommand.php index 8cdf0e9c..afabae09 100644 --- a/src/Command/SelfUpdateCommand.php +++ b/src/Command/SelfUpdateCommand.php @@ -4,6 +4,8 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use Composer\Util\AuthHelper; use Composer\Util\HttpDownloader; use Php\Pie\ComposerIntegration\PieComposerFactory; @@ -46,7 +48,8 @@ final class SelfUpdateCommand extends Command /** @param non-empty-string $githubApiBaseUrl */ public function __construct( private readonly string $githubApiBaseUrl, - private readonly QuieterConsoleIO $io, + private readonly IOInterface $io, + private readonly QuieterConsoleIO $quieterConsoleIo, private readonly ContainerInterface $container, private readonly FullPathToSelf $fullPathToSelf, ) { @@ -81,7 +84,7 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { if (! PieVersion::isPharBuild()) { - $output->writeln('Aborting! You are not running a PHAR, cannot self-update.'); + $this->io->write('Aborting! You are not running a PHAR, cannot self-update.'); return Command::FAILURE; } @@ -100,20 +103,20 @@ public function execute(InputInterface $input, OutputInterface $output): int $updateChannel = Channel::Stable; } - $output->writeln(sprintf('Updating using the %s channel.', $updateChannel->value)); + $this->io->write(sprintf('Updating using the %s channel.', $updateChannel->value)); - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); $composer = PieComposerFactory::createPieComposer( $this->container, PieComposerRequest::noOperation( - $output, + new NullIO(), $targetPlatform, ), ); - $httpDownloader = new HttpDownloader($this->io, $composer->getConfig()); - $authHelper = new AuthHelper($this->io, $composer->getConfig()); + $httpDownloader = new HttpDownloader($this->quieterConsoleIo, $composer->getConfig()); + $authHelper = new AuthHelper($this->quieterConsoleIo, $composer->getConfig()); $fetchLatestPieRelease = new FetchPieReleaseFromGitHub($this->githubApiBaseUrl, $httpDownloader, $authHelper); $verifyPiePhar = VerifyPieReleaseUsingAttestation::factory(); @@ -123,22 +126,22 @@ public function execute(InputInterface $input, OutputInterface $output): int 'https://php.github.io/pie/pie-nightly.phar', ); - $output->writeln('Downloading the latest nightly release.'); + $this->io->write('Downloading the latest nightly release.'); } else { try { $latestRelease = $fetchLatestPieRelease->latestReleaseMetadata($updateChannel); } catch (Throwable $throwable) { - $output->writeln(sprintf('%s', $throwable->getMessage())); + $this->io->write(sprintf('%s', $throwable->getMessage())); return Command::FAILURE; } $pieVersion = PieVersion::get(); - $output->writeln(sprintf('You are currently running PIE version %s', $pieVersion)); + $this->io->write(sprintf('You are currently running PIE version %s', $pieVersion)); if (! ReleaseIsNewer::forChannel($updateChannel, $pieVersion, $latestRelease)) { - $output->writeln(sprintf( + $this->io->write(sprintf( 'You already have the latest version for the %s channel 😍', $updateChannel->value, )); @@ -146,43 +149,43 @@ public function execute(InputInterface $input, OutputInterface $output): int return Command::SUCCESS; } - $output->writeln( + $this->io->write( sprintf('Newer version %s found, going to update you... ⏳', $latestRelease->tag), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); } $pharFilename = $fetchLatestPieRelease->downloadContent($latestRelease); - $output->writeln( + $this->io->write( sprintf('Verifying release with digest sha256:%s...', $pharFilename->checksum), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); try { - $verifyPiePhar->verify($latestRelease, $pharFilename, $output); + $verifyPiePhar->verify($latestRelease, $pharFilename, $this->io); } catch (FailedToVerifyRelease $failedToVerifyRelease) { - $output->writeln(sprintf( + $this->io->write(sprintf( '❌ Failed to verify the pie.phar release %s: %s', $latestRelease->tag, $failedToVerifyRelease->getMessage(), )); - $output->writeln('This means I could not verify that the PHAR we tried to update to was authentic, so I am aborting the self-update.'); + $this->io->write('This means I could not verify that the PHAR we tried to update to was authentic, so I am aborting the self-update.'); unlink($pharFilename->filePath); return Command::FAILURE; } $fullPathToSelf = ($this->fullPathToSelf)(); - $output->writeln( + $this->io->write( sprintf('Writing new version to %s', $fullPathToSelf), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); SudoFilePut::contents($fullPathToSelf, file_get_contents($pharFilename->filePath)); unlink($pharFilename->filePath); - $output->writeln(sprintf( + $this->io->write(sprintf( '%s PIE has been upgraded to %s', Emoji::GREEN_CHECKMARK, $latestRelease->tag, diff --git a/src/Command/SelfVerifyCommand.php b/src/Command/SelfVerifyCommand.php index a024d0d6..f388287d 100644 --- a/src/Command/SelfVerifyCommand.php +++ b/src/Command/SelfVerifyCommand.php @@ -4,6 +4,7 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; use Php\Pie\File\BinaryFile; use Php\Pie\File\FullPathToSelf; use Php\Pie\SelfManage\Update\ReleaseMetadata; @@ -26,6 +27,7 @@ final class SelfVerifyCommand extends Command { public function __construct( private readonly FullPathToSelf $fullPathToSelf, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -40,7 +42,7 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { if (! PieVersion::isPharBuild()) { - $output->writeln('Aborting! You are not running a PHAR, cannot self-verify.'); + $this->io->write('Aborting! You are not running a PHAR, cannot self-verify.'); return Command::FAILURE; } @@ -50,9 +52,9 @@ public function execute(InputInterface $input, OutputInterface $output): int $verifyPiePhar = VerifyPieReleaseUsingAttestation::factory(); try { - $verifyPiePhar->verify($latestRelease, $pharFilename, $output); + $verifyPiePhar->verify($latestRelease, $pharFilename, $this->io); } catch (FailedToVerifyRelease $failedToVerifyRelease) { - $output->writeln(sprintf( + $this->io->write(sprintf( '❌ Failed to verify the pie.phar release %s: %s', $latestRelease->tag, $failedToVerifyRelease->getMessage(), @@ -61,7 +63,7 @@ public function execute(InputInterface $input, OutputInterface $output): int return Command::FAILURE; } - $output->writeln(sprintf( + $this->io->write(sprintf( '%s You are running an authentic PIE version %s.', Emoji::GREEN_CHECKMARK, $latestRelease->tag, diff --git a/src/Command/ShowCommand.php b/src/Command/ShowCommand.php index 1227c458..cfe0caaa 100644 --- a/src/Command/ShowCommand.php +++ b/src/Command/ShowCommand.php @@ -4,6 +4,8 @@ namespace Php\Pie\Command; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use Php\Pie\ComposerIntegration\PieComposerFactory; use Php\Pie\ComposerIntegration\PieComposerRequest; use Php\Pie\ComposerIntegration\PieInstalledJsonMetadataKeys; @@ -22,7 +24,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; use Webmozart\Assert\Assert; @@ -50,6 +51,7 @@ public function __construct( private readonly InstalledPiePackages $installedPiePackages, private readonly ContainerInterface $container, private readonly ResolveDependencyWithComposer $resolveDependencyWithComposer, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -71,25 +73,24 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { $showAll = $input->hasOption(self::OPTION_ALL) && $input->getOption(self::OPTION_ALL); - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); - - if ($output->getVerbosity() < OutputInterface::VERBOSITY_VERBOSE) { - $output->writeln( - sprintf( - 'Using pie.json: %s', - PiePlatform::getPieJsonFilename($targetPlatform), - ), - ); - } + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); + + $this->io->write( + sprintf( + 'Using pie.json: %s', + PiePlatform::getPieJsonFilename($targetPlatform), + ), + verbosity: IOInterface::VERBOSE, + ); if (! $showAll) { - $output->writeln('Tip: to include extensions in this list that PIE does not manage, use the --all flag.'); + $this->io->write('Tip: to include extensions in this list that PIE does not manage, use the --all flag.'); } $composer = PieComposerFactory::createPieComposer( $this->container, PieComposerRequest::noOperation( - new NullOutput(), + new NullIO(), $targetPlatform, ), ); @@ -101,16 +102,16 @@ public function execute(InputInterface $input, OutputInterface $output): int $piePackagesMatched = []; $rootPackageRequires = $composer->getPackage()->getRequires(); - $output->writeln(sprintf( + $this->io->write(sprintf( "\n" . '%s:', $showAll ? 'All loaded extensions' : 'Loaded PIE extensions', )); array_walk( $phpEnabledExtensions, - function (string $version, string $phpExtensionName) use ($composer, $rootPackageRequires, $targetPlatform, $showAll, $output, $piePackages, $extensionPath, $extensionEnding, &$piePackagesMatched): void { + function (string $version, string $phpExtensionName) use ($composer, $rootPackageRequires, $targetPlatform, $showAll, $piePackages, $extensionPath, $extensionEnding, &$piePackagesMatched): void { if (! array_key_exists($phpExtensionName, $piePackages)) { if ($showAll) { - $output->writeln(sprintf(' %s:%s', $phpExtensionName, $version)); + $this->io->write(sprintf(' %s:%s', $phpExtensionName, $version)); } return; @@ -156,7 +157,7 @@ function (string $version, string $phpExtensionName) use ($composer, $rootPackag $updateNotice .= sprintf(', latest version is %s', $latestPackage->version()); } - $output->writeln(sprintf( + $this->io->write(sprintf( ' %s:%s (from 🥧 %s%s)%s', $phpExtensionName, $version, @@ -173,21 +174,21 @@ function (string $version, string $phpExtensionName) use ($composer, $rootPackag ); if (! $showAll && ! count($piePackagesMatched)) { - $output->writeln('(none)'); + $this->io->write('(none)'); } $unmatchedPiePackages = array_diff(array_keys($piePackages), $piePackagesMatched); if (count($unmatchedPiePackages)) { - $output->writeln(sprintf( + $this->io->write(sprintf( '%s %s PIE packages not loaded:', "\n", Emoji::WARNING, )); - $output->writeln('These extensions were installed with PIE but are not currently enabled.' . "\n"); + $this->io->write('These extensions were installed with PIE but are not currently enabled.' . "\n"); foreach ($unmatchedPiePackages as $unmatchedPiePackage) { - $output->writeln(sprintf(' - %s', $piePackages[$unmatchedPiePackage]->prettyNameAndVersion())); + $this->io->write(sprintf(' - %s', $piePackages[$unmatchedPiePackage]->prettyNameAndVersion())); } } diff --git a/src/Command/UninstallCommand.php b/src/Command/UninstallCommand.php index 0cd46dc9..e2aab0b8 100644 --- a/src/Command/UninstallCommand.php +++ b/src/Command/UninstallCommand.php @@ -5,6 +5,8 @@ namespace Php\Pie\Command; use Composer\Composer; +use Composer\IO\IOInterface; +use Composer\IO\NullIO; use Php\Pie\ComposerIntegration\ComposerIntegrationHandler; use Php\Pie\ComposerIntegration\PieComposerFactory; use Php\Pie\ComposerIntegration\PieComposerRequest; @@ -18,7 +20,6 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\NullOutput; use Symfony\Component\Console\Output\OutputInterface; use Webmozart\Assert\Assert; @@ -34,6 +35,7 @@ public function __construct( private readonly InstalledPiePackages $installedPiePackages, private readonly ContainerInterface $container, private readonly ComposerIntegrationHandler $composerIntegrationHandler, + private readonly IOInterface $io, ) { parent::__construct(); } @@ -54,19 +56,19 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { if (! TargetPlatform::isRunningAsRoot()) { - $output->writeln('This command may need elevated privileges, and may prompt you for your password.'); + $this->io->write('This command may need elevated privileges, and may prompt you for your password.'); } $packageToRemove = (string) $input->getArgument(self::ARG_PACKAGE_NAME); Assert::stringNotEmpty($packageToRemove); $requestedPackageAndVersionToRemove = new RequestedPackageAndVersion($packageToRemove, null); - $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output); + $targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $this->io); $composer = PieComposerFactory::createPieComposer( $this->container, PieComposerRequest::noOperation( - new NullOutput(), + new NullIO(), $targetPlatform, ), ); @@ -74,7 +76,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $piePackage = $this->findPiePackageByPackageName($packageToRemove, $composer); if ($piePackage === null) { - $output->writeln('No package found: ' . $packageToRemove . ''); + $this->io->write('No package found: ' . $packageToRemove . ''); return 1; } @@ -82,7 +84,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $composer = PieComposerFactory::createPieComposer( $this->container, new PieComposerRequest( - $output, + $this->io, $targetPlatform, $requestedPackageAndVersionToRemove, PieOperation::Uninstall, diff --git a/src/ComposerIntegration/InstallAndBuildProcess.php b/src/ComposerIntegration/InstallAndBuildProcess.php index a616e77f..b036b24c 100644 --- a/src/ComposerIntegration/InstallAndBuildProcess.php +++ b/src/ComposerIntegration/InstallAndBuildProcess.php @@ -29,14 +29,14 @@ public function __invoke( CompletePackageInterface $composerPackage, string $installPath, ): void { - $output = $composerRequest->pieOutput; + $io = $composerRequest->pieOutput; $downloadedPackage = DownloadedPackage::fromPackageAndExtractedPath( Package::fromComposerCompletePackage($composerPackage), $installPath, ); - $output->writeln(sprintf( + $io->write(sprintf( 'Extracted %s source to: %s', $downloadedPackage->package->prettyNameAndVersion(), $downloadedPackage->extractedSourcePath, @@ -53,7 +53,7 @@ public function __invoke( $downloadedPackage, $composerRequest->targetPlatform, $composerRequest->configureOptions, - $output, + $io, $composerRequest->phpizePath, ); @@ -75,7 +75,7 @@ public function __invoke( ($this->pieInstall)( $downloadedPackage, $composerRequest->targetPlatform, - $output, + $io, $composerRequest->attemptToSetupIniFile, ), ); diff --git a/src/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListener.php b/src/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListener.php index a3a57319..3b949515 100644 --- a/src/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListener.php +++ b/src/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListener.php @@ -95,7 +95,7 @@ function (OperationInterface $operation): void { $possibleAssetNames, ); - $this->composerRequest->pieOutput->writeln('Found prebuilt archive: ' . $url); + $this->composerRequest->pieOutput->write('Found prebuilt archive: ' . $url); $composerPackage->setDistUrl($url); if (pathinfo($url, PATHINFO_EXTENSION) !== 'tgz') { diff --git a/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php b/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php index a364888f..0e290d66 100644 --- a/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php +++ b/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php @@ -12,8 +12,8 @@ use Composer\DependencyResolver\Transaction; use Composer\Installer\InstallerEvent; use Composer\Installer\InstallerEvents; +use Composer\IO\IOInterface; use Php\Pie\ComposerIntegration\PieComposerRequest; -use Symfony\Component\Console\Output\OutputInterface; use function array_filter; use function assert; @@ -47,12 +47,12 @@ public function __invoke(InstallerEvent $installerEvent): void $installerEvent->getTransaction()?->getOperations() ?? [], function (OperationInterface $operation) use ($pieOutput): bool { if (! $operation instanceof InstallOperation && ! $operation instanceof UninstallOperation) { - $pieOutput->writeln( + $pieOutput->write( sprintf( 'Unexpected operation during installer: %s', $operation::class, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); return false; @@ -61,12 +61,12 @@ function (OperationInterface $operation) use ($pieOutput): bool { $isRequestedPiePackage = $this->composerRequest->requestedPackage->package === $operation->getPackage()->getName(); if (! $isRequestedPiePackage) { - $pieOutput->writeln( + $pieOutput->write( sprintf( 'Filtering package %s from install operations, as it was not the requested package', $operation->getPackage()->getName(), ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); } diff --git a/src/ComposerIntegration/PieComposerRequest.php b/src/ComposerIntegration/PieComposerRequest.php index ef6697cd..20de4ac2 100644 --- a/src/ComposerIntegration/PieComposerRequest.php +++ b/src/ComposerIntegration/PieComposerRequest.php @@ -4,10 +4,10 @@ namespace Php\Pie\ComposerIntegration; +use Composer\IO\IOInterface; use Php\Pie\DependencyResolver\RequestedPackageAndVersion; use Php\Pie\Platform\TargetPhp\PhpizePath; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; /** * @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks @@ -18,7 +18,7 @@ final class PieComposerRequest { /** @param list $configureOptions */ public function __construct( - public readonly OutputInterface $pieOutput, + public readonly IOInterface $pieOutput, public readonly TargetPlatform $targetPlatform, public readonly RequestedPackageAndVersion $requestedPackage, public readonly PieOperation $operation, @@ -33,7 +33,7 @@ public function __construct( * for example just reading metadata about the installed system. */ public static function noOperation( - OutputInterface $pieOutput, + IOInterface $pieOutput, TargetPlatform $targetPlatform, ): self { return new PieComposerRequest( diff --git a/src/ComposerIntegration/PiePackageInstaller.php b/src/ComposerIntegration/PiePackageInstaller.php index db6b025c..0a218708 100644 --- a/src/ComposerIntegration/PiePackageInstaller.php +++ b/src/ComposerIntegration/PiePackageInstaller.php @@ -12,7 +12,6 @@ use Composer\Repository\InstalledRepositoryInterface; use Composer\Util\Filesystem; use Php\Pie\ExtensionType; -use Symfony\Component\Console\Output\OutputInterface; use function sprintf; @@ -38,23 +37,23 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa return parent::install($repo, $composerPackage) ?->then(function () use ($composerPackage) { - $output = $this->composerRequest->pieOutput; + $io = $this->composerRequest->pieOutput; if ($this->composerRequest->requestedPackage->package !== $composerPackage->getName()) { - $output->writeln( + $io->write( sprintf( 'Skipping %s install request from Composer as it was not the expected PIE package %s', $composerPackage->getName(), $this->composerRequest->requestedPackage->package, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); return null; } if (! $composerPackage instanceof CompletePackage) { - $output->writeln(sprintf( + $io->write(sprintf( 'Not using PIE to install %s as it was not a Complete Package', $composerPackage->getName(), )); @@ -80,23 +79,23 @@ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $ return parent::uninstall($repo, $composerPackage) ?->then(function () use ($composerPackage) { - $output = $this->composerRequest->pieOutput; + $io = $this->composerRequest->pieOutput; if ($this->composerRequest->requestedPackage->package !== $composerPackage->getName()) { - $output->writeln( + $io->write( sprintf( 'Skipping %s uninstall request from Composer as it was not the expected PIE package %s', $composerPackage->getName(), $this->composerRequest->requestedPackage->package, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); return null; } if (! $composerPackage instanceof CompletePackage) { - $output->writeln(sprintf( + $io->write(sprintf( 'Not using PIE to install %s as it was not a Complete Package', $composerPackage->getName(), )); diff --git a/src/ComposerIntegration/UninstallProcess.php b/src/ComposerIntegration/UninstallProcess.php index c461928c..7b259754 100644 --- a/src/ComposerIntegration/UninstallProcess.php +++ b/src/ComposerIntegration/UninstallProcess.php @@ -4,11 +4,11 @@ namespace Php\Pie\ComposerIntegration; +use Composer\IO\IOInterface; use Composer\Package\CompletePackageInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Installing\Ini\RemoveIniEntry; use Php\Pie\Installing\Uninstall; -use Symfony\Component\Console\Output\OutputInterface; use function array_walk; use function count; @@ -28,30 +28,30 @@ public function __invoke( PieComposerRequest $composerRequest, CompletePackageInterface $composerPackage, ): void { - $output = $composerRequest->pieOutput; + $io = $composerRequest->pieOutput; $piePackage = Package::fromComposerCompletePackage($composerPackage); - $affectedIniFiles = ($this->removeIniEntry)($piePackage, $composerRequest->targetPlatform, $output); + $affectedIniFiles = ($this->removeIniEntry)($piePackage, $composerRequest->targetPlatform, $io); if (count($affectedIniFiles) === 1) { - $output->writeln( + $io->write( sprintf('INI file "%s" was updated to remove the extension.', reset($affectedIniFiles)), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); } elseif (count($affectedIniFiles) === 0) { - $output->writeln( + $io->write( 'No INI files were updated to remove the extension.', - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); } else { - $output->writeln( + $io->write( 'The following INI files were updated to remove the extnesion:', - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); - array_walk($affectedIniFiles, static fn (string $ini) => $output->writeln(' - ' . $ini)); + array_walk($affectedIniFiles, static fn (string $ini) => $io->write(' - ' . $ini)); } - $output->writeln(sprintf('👋 Removed extension: %s', ($this->uninstall)($piePackage)->filePath)); + $io->write(sprintf('👋 Removed extension: %s', ($this->uninstall)($piePackage)->filePath)); } } diff --git a/src/ComposerIntegration/VendorCleanup.php b/src/ComposerIntegration/VendorCleanup.php index c16d073e..52d63216 100644 --- a/src/ComposerIntegration/VendorCleanup.php +++ b/src/ComposerIntegration/VendorCleanup.php @@ -5,8 +5,8 @@ namespace Php\Pie\ComposerIntegration; use Composer\Composer; +use Composer\IO\IOInterface; use Composer\Util\Filesystem; -use Symfony\Component\Console\Output\OutputInterface; use function array_filter; use function array_walk; @@ -21,7 +21,7 @@ class VendorCleanup { public function __construct( - private readonly OutputInterface $output, + private readonly IOInterface $io, private readonly Filesystem $filesystem, ) { } @@ -32,12 +32,12 @@ public function __invoke(Composer $composer): void $vendorContents = scandir($vendorDir); if (! is_array($vendorContents)) { - $this->output->writeln( + $this->io->write( sprintf( 'Vendor directory (vendor-dir config) %s seemed invalid?', $vendorDir, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); return; @@ -63,24 +63,24 @@ static function (string $path): bool { function (string $pathToRemove) use ($vendorDir): void { $fullPathToRemove = $vendorDir . DIRECTORY_SEPARATOR . $pathToRemove; - $this->output->writeln( + $this->io->write( sprintf( 'Removing: %s', $fullPathToRemove, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); if ($this->filesystem->remove($fullPathToRemove)) { return; } - $this->output->writeln( + $this->io->write( sprintf( 'Warning: failed to remove %s', $fullPathToRemove, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); }, ); diff --git a/src/Container.php b/src/Container.php index ef10f42e..2815650c 100644 --- a/src/Container.php +++ b/src/Container.php @@ -5,6 +5,9 @@ namespace Php\Pie; use Composer\Factory; +use Composer\IO\BufferIO; +use Composer\IO\ConsoleIO; +use Composer\IO\IOInterface; use Composer\Util\Platform as ComposerPlatform; use Illuminate\Container\Container as IlluminateContainer; use Php\Pie\Building\Build; @@ -46,12 +49,15 @@ use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\EventDispatcher\EventDispatcher; +use function assert; use function getcwd; use function str_starts_with; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ final class Container { + private static BufferIO|null $testBuffer = null; + public static function factory(): ContainerInterface { $container = new IlluminateContainer(); @@ -78,12 +84,14 @@ static function (): OutputInterface { ); }, ); - $container->singleton(EventDispatcher::class, static function () { + $container->singleton(EventDispatcher::class, static function (ContainerInterface $container) { + $io = $container->get(IOInterface::class); + $displayedBanner = false; $eventDispatcher = new EventDispatcher(); $eventDispatcher->addListener( ConsoleEvents::COMMAND, - static function (ConsoleCommandEvent $event) use (&$displayedBanner): void { + static function (ConsoleCommandEvent $event) use (&$displayedBanner, $io): void { $command = $event->getCommand(); $application = $command?->getApplication(); @@ -92,7 +100,7 @@ static function (ConsoleCommandEvent $event) use (&$displayedBanner): void { } $displayedBanner = true; - $event->getOutput()->writeln($application->getLongVersion() . ', from The PHP Foundation'); + $io->write($application->getLongVersion() . ', from The PHP Foundation'); }, ); @@ -112,6 +120,17 @@ static function (ConsoleCommandEvent $event) use (&$displayedBanner): void { $container->singleton(SelfVerifyCommand::class); $container->singleton(InstallExtensionsForProjectCommand::class); + $container->singleton(IOInterface::class, static function (ContainerInterface $container): IOInterface { + return new ConsoleIO( + $container->get(InputInterface::class), + $container->get(OutputInterface::class), + new MinimalHelperSet( + [ + 'question' => new QuestionHelper(), + ], + ), + ); + }); $container->singleton(QuieterConsoleIO::class, static function (ContainerInterface $container): QuieterConsoleIO { return new QuieterConsoleIO( $container->get(InputInterface::class), @@ -184,4 +203,25 @@ static function (ContainerInterface $container): Install { return $container; } + + public static function testFactory(): ContainerInterface + { + self::$testBuffer ??= new BufferIO(); + + $container = self::factory(); + assert($container instanceof IlluminateContainer); + + $container->singleton(IOInterface::class, static function () { + return self::$testBuffer; + }); + + return $container; + } + + public static function testBuffer(): BufferIO + { + assert(self::$testBuffer !== null); + + return self::$testBuffer; + } } diff --git a/src/DependencyResolver/ResolveDependencyWithComposer.php b/src/DependencyResolver/ResolveDependencyWithComposer.php index 7546d32d..74082b71 100644 --- a/src/DependencyResolver/ResolveDependencyWithComposer.php +++ b/src/DependencyResolver/ResolveDependencyWithComposer.php @@ -6,13 +6,13 @@ use Composer\Composer; use Composer\Filter\PlatformRequirementFilter\PlatformRequirementFilterFactory; +use Composer\IO\IOInterface; use Composer\Package\CompletePackageInterface; use Php\Pie\ComposerIntegration\QuieterConsoleIO; use Php\Pie\ComposerIntegration\VersionSelectorFactory; use Php\Pie\ExtensionType; use Php\Pie\Platform\TargetPlatform; use Php\Pie\Platform\ThreadSafetyMode; -use Symfony\Component\Console\Output\OutputInterface; use function in_array; use function preg_match; @@ -22,7 +22,7 @@ final class ResolveDependencyWithComposer implements DependencyResolver { public function __construct( - private readonly OutputInterface $output, + private readonly IOInterface $io, private readonly QuieterConsoleIO $arrayCollectionIo, ) { } @@ -115,7 +115,7 @@ private function assertBuildProviderProvidersBundledExtensions(TargetPlatform $t if ($buildProvider === 'https://github.com/docker-library/php') { $identifiedBuildProvider = true; - $this->output->writeln(sprintf( + $this->io->write(sprintf( '%sYou should probably use "docker-php-ext-install %s" instead', $note, $piePackage->extensionName()->name(), @@ -124,7 +124,7 @@ private function assertBuildProviderProvidersBundledExtensions(TargetPlatform $t if ($buildProvider === 'Debian') { $identifiedBuildProvider = true; - $this->output->writeln(sprintf( + $this->io->write(sprintf( '%sYou should probably use "apt install php%s-%s" or "apt install php-%s" (or similar) instead', $note, $targetPlatform->phpBinaryPath->majorMinorVersion(), @@ -135,7 +135,7 @@ private function assertBuildProviderProvidersBundledExtensions(TargetPlatform $t if ($buildProvider === 'Remi\'s RPM repository #StandWithUkraine') { $identifiedBuildProvider = true; - $this->output->writeln(sprintf( + $this->io->write(sprintf( '%sYou should probably use "dnf install php-%s" instead', $note, $piePackage->extensionName()->name(), diff --git a/src/Installing/Ini/AddExtensionToTheIniFile.php b/src/Installing/Ini/AddExtensionToTheIniFile.php index 12b68079..f62ccb04 100644 --- a/src/Installing/Ini/AddExtensionToTheIniFile.php +++ b/src/Installing/Ini/AddExtensionToTheIniFile.php @@ -4,12 +4,12 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\ExtensionType; use Php\Pie\File\Sudo; use Php\Pie\File\SudoFilePut; use Php\Pie\Platform\TargetPhp\PhpBinaryPath; -use Symfony\Component\Console\Output\OutputInterface; use Throwable; use function file_get_contents; @@ -28,28 +28,28 @@ public function __invoke( string $ini, Package $package, PhpBinaryPath $phpBinaryPath, - OutputInterface $output, + IOInterface $io, callable|null $additionalEnableStep, ): bool { if (! is_writable($ini) && ! Sudo::exists()) { - $output->writeln( + $io->write( sprintf( 'PHP is configured to use %s, but it is not writable by PIE.', $ini, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; } if (! is_readable($ini)) { - $output->writeln( + $io->write( sprintf( 'Could not read %s to make a backup of it, aborting enablement of extension', $ini, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; @@ -58,12 +58,12 @@ public function __invoke( $originalIniContent = file_get_contents($ini); if (! is_string($originalIniContent)) { - $output->writeln( + $io->write( sprintf( 'Tried making a backup of %s but could not read it, aborting enablement of extension', $ini, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; @@ -74,26 +74,26 @@ public function __invoke( $ini, $originalIniContent . $this->iniFileContent($package), ); - $output->writeln( + $io->write( sprintf( 'Enabled extension %s in the INI file %s', $package->extensionName()->name(), $ini, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); if ($additionalEnableStep !== null && ! $additionalEnableStep()) { return false; } - $phpBinaryPath->assertExtensionIsLoadedInRuntime($package->extensionName(), $output); + $phpBinaryPath->assertExtensionIsLoadedInRuntime($package->extensionName(), $io); return true; } catch (Throwable $anything) { SudoFilePut::contents($ini, $originalIniContent); - $output->writeln(sprintf( + $io->write(sprintf( 'Something went wrong enabling the %s extension: %s', $package->extensionName()->name(), $anything->getMessage(), diff --git a/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php b/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php index 1c3a57e7..9c587389 100644 --- a/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php +++ b/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php @@ -4,9 +4,9 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; use Throwable; use function file_exists; @@ -30,28 +30,28 @@ public function __invoke( string $iniFile, TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, - OutputInterface $output, + IOInterface $io, callable|null $additionalEnableStep, ): bool { if (! file_exists($iniFile) || ! is_readable($iniFile)) { - $output->writeln( + $io->write( sprintf( 'PHP is configured to use %s, but it did not exist, or is not readable by PIE.', $iniFile, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; } if (($this->isExtensionAlreadyInTheIniFile)($iniFile, $downloadedPackage->package->extensionName())) { - $output->writeln( + $io->write( sprintf( 'Extension is already enabled in the INI file %s', $iniFile, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); if ($additionalEnableStep !== null && ! $additionalEnableStep()) { @@ -59,11 +59,11 @@ public function __invoke( } try { - $targetPlatform->phpBinaryPath->assertExtensionIsLoadedInRuntime($downloadedPackage->package->extensionName(), $output); + $targetPlatform->phpBinaryPath->assertExtensionIsLoadedInRuntime($downloadedPackage->package->extensionName(), $io); return true; } catch (Throwable $anything) { - $output->writeln(sprintf( + $io->write(sprintf( 'Something went wrong verifying the %s extension is enabled: %s', $downloadedPackage->package->extensionName()->name(), $anything->getMessage(), @@ -77,7 +77,7 @@ public function __invoke( $iniFile, $downloadedPackage->package, $targetPlatform->phpBinaryPath, - $output, + $io, $additionalEnableStep, ); } diff --git a/src/Installing/Ini/DockerPhpExtEnable.php b/src/Installing/Ini/DockerPhpExtEnable.php index 588a5e02..56135279 100644 --- a/src/Installing/Ini/DockerPhpExtEnable.php +++ b/src/Installing/Ini/DockerPhpExtEnable.php @@ -4,12 +4,12 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPhp\Exception\ExtensionIsNotLoaded; use Php\Pie\Platform\TargetPlatform; use Php\Pie\Util\Process; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\ProcessFailedException; use function sprintf; @@ -32,7 +32,7 @@ public function setup( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, ): bool { $dockerPhpExtEnable = $this->dockerPhpExtEnablePath(); @@ -43,14 +43,14 @@ public function setup( try { $enableOutput = Process::run([$dockerPhpExtEnable, $downloadedPackage->package->extensionName()->name()]); } catch (ProcessFailedException $processFailed) { - $output->writeln( + $io->write( sprintf( 'Could not enable extension %s using %s. Exception was: %s', $downloadedPackage->package->extensionName()->name(), $this->dockerPhpExtEnableName, $processFailed->getMessage(), ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; @@ -59,19 +59,19 @@ public function setup( try { $targetPlatform->phpBinaryPath->assertExtensionIsLoadedInRuntime( $downloadedPackage->package->extensionName(), - $output, + $io, ); return true; } catch (ExtensionIsNotLoaded) { - $output->writeln( + $io->write( sprintf( 'Asserting that extension %s was enabled using %s failed. Output was: %s', $downloadedPackage->package->extensionName()->name(), $this->dockerPhpExtEnableName, $enableOutput !== '' ? $enableOutput : '(empty)', ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; diff --git a/src/Installing/Ini/OndrejPhpenmod.php b/src/Installing/Ini/OndrejPhpenmod.php index bc39ab67..a167b562 100644 --- a/src/Installing/Ini/OndrejPhpenmod.php +++ b/src/Installing/Ini/OndrejPhpenmod.php @@ -4,6 +4,7 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Composer\Util\Platform; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; @@ -12,7 +13,6 @@ use Php\Pie\File\SudoUnlink; use Php\Pie\Platform\TargetPlatform; use Php\Pie\Util\Process; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\ProcessFailedException; use function array_unshift; @@ -47,7 +47,7 @@ public function setup( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, ): bool { $phpenmodPath = $this->phpenmodPath(); @@ -60,9 +60,9 @@ public function setup( $additionalPhpIniPath = $targetPlatform->phpBinaryPath->additionalIniDirectory(); if ($additionalPhpIniPath === null) { - $output->writeln( + $io->write( 'Additional INI file path was not set - may not be Ondrej PHP repo', - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; @@ -70,36 +70,36 @@ public function setup( // Cursory check for the expected PHP INI directory; this is another indication we're using the Ondrej repo if (preg_match('#/etc/php/\d\.\d/[a-z-_]+/conf.d#', $additionalPhpIniPath) !== 1) { - $output->writeln( + $io->write( sprintf( 'Warning: additional INI file path was not in the expected format (/etc/php/{version}/{sapi}/conf.d). Path was: %s', $additionalPhpIniPath, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); } $expectedModsAvailablePath = sprintf($this->modsAvailablePath, $targetPlatform->phpBinaryPath->majorMinorVersion()); if (! file_exists($expectedModsAvailablePath)) { - $output->writeln( + $io->write( sprintf( 'Mods available path %s does not exist', $expectedModsAvailablePath, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; } if (! is_dir($expectedModsAvailablePath)) { - $output->writeln( + $io->write( sprintf( 'Mods available path %s is not a directory', $expectedModsAvailablePath, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; @@ -108,12 +108,12 @@ public function setup( $needSudo = false; if (! is_writable($expectedModsAvailablePath)) { if (! Sudo::exists()) { - $output->writeln( + $io->write( sprintf( 'Mods available path %s is not writable', $expectedModsAvailablePath, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; @@ -131,12 +131,12 @@ public function setup( $pieCreatedTheIniFile = false; if (! file_exists($expectedIniFile)) { - $output->writeln( + $io->write( sprintf( 'Creating new INI file based on extension priority: %s', $expectedIniFile, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); $pieCreatedTheIniFile = true; SudoCreate::file($expectedIniFile); @@ -146,8 +146,8 @@ public function setup( $expectedIniFile, $targetPlatform, $downloadedPackage, - $output, - static function () use ($needSudo, $phpenmodPath, $targetPlatform, $downloadedPackage, $output): bool { + $io, + static function () use ($needSudo, $phpenmodPath, $targetPlatform, $downloadedPackage, $io): bool { try { $processArgs = [ $phpenmodPath, @@ -159,7 +159,10 @@ static function () use ($needSudo, $phpenmodPath, $targetPlatform, $downloadedPa ]; if ($needSudo && Sudo::exists()) { - $output->writeln('Using sudo to elevate privileges for phpenmod', OutputInterface::VERBOSITY_VERBOSE); + $io->write( + 'Using sudo to elevate privileges for phpenmod', + verbosity: IOInterface::VERBOSE, + ); array_unshift($processArgs, Sudo::find()); } @@ -167,7 +170,7 @@ static function () use ($needSudo, $phpenmodPath, $targetPlatform, $downloadedPa return true; } catch (ProcessFailedException $processFailedException) { - $output->writeln( + $io->write( sprintf( 'Failed to use %s to enable %s for PHP %s: %s', $phpenmodPath, @@ -175,7 +178,7 @@ static function () use ($needSudo, $phpenmodPath, $targetPlatform, $downloadedPa $targetPlatform->phpBinaryPath->majorMinorVersion(), $processFailedException->getMessage(), ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; diff --git a/src/Installing/Ini/PickBestSetupIniApproach.php b/src/Installing/Ini/PickBestSetupIniApproach.php index a122491e..ca85312f 100644 --- a/src/Installing/Ini/PickBestSetupIniApproach.php +++ b/src/Installing/Ini/PickBestSetupIniApproach.php @@ -4,11 +4,11 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPlatform; use ReflectionClass; -use Symfony\Component\Console\Output\OutputInterface; use function array_filter; use function array_values; @@ -49,30 +49,36 @@ public function setup( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, ): bool { $approaches = $this->approachesThatCanBeUsed($targetPlatform); if (count($approaches) === 0) { - $output->writeln('No INI setup approaches can be used on this platform.', OutputInterface::VERBOSITY_VERBOSE); + $io->write( + 'No INI setup approaches can be used on this platform.', + verbosity: IOInterface::VERBOSE, + ); return false; } foreach ($approaches as $approach) { - $output->writeln( + $io->write( sprintf( 'Trying to enable extension using %s', (new ReflectionClass($approach))->getShortName(), ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); - if ($approach->setup($targetPlatform, $downloadedPackage, $binaryFile, $output)) { + if ($approach->setup($targetPlatform, $downloadedPackage, $binaryFile, $io)) { return true; } } - $output->writeln('None of the INI setup approaches succeeded.', OutputInterface::VERBOSITY_VERBOSE); + $io->write( + 'None of the INI setup approaches succeeded.', + verbosity: IOInterface::VERBOSE, + ); return false; } diff --git a/src/Installing/Ini/PreCheckExtensionAlreadyLoaded.php b/src/Installing/Ini/PreCheckExtensionAlreadyLoaded.php index 23c8b0ce..1a5a21b6 100644 --- a/src/Installing/Ini/PreCheckExtensionAlreadyLoaded.php +++ b/src/Installing/Ini/PreCheckExtensionAlreadyLoaded.php @@ -4,11 +4,11 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPhp\Exception\ExtensionIsNotLoaded; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ class PreCheckExtensionAlreadyLoaded implements SetupIniApproach @@ -22,12 +22,12 @@ public function setup( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, ): bool { try { $targetPlatform->phpBinaryPath->assertExtensionIsLoadedInRuntime( $downloadedPackage->package->extensionName(), - $output, + $io, ); return true; diff --git a/src/Installing/Ini/RemoveIniEntry.php b/src/Installing/Ini/RemoveIniEntry.php index 347c9168..61473b26 100644 --- a/src/Installing/Ini/RemoveIniEntry.php +++ b/src/Installing/Ini/RemoveIniEntry.php @@ -4,13 +4,13 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ interface RemoveIniEntry { /** @return list Returns a list of INI files that were updated to remove the extension */ - public function __invoke(Package $package, TargetPlatform $targetPlatform, OutputInterface $output): array; + public function __invoke(Package $package, TargetPlatform $targetPlatform, IOInterface $io): array; } diff --git a/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php b/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php index adee0753..fd826223 100644 --- a/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php +++ b/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php @@ -4,12 +4,12 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\ExtensionType; use Php\Pie\File\FailedToWriteFile; use Php\Pie\File\SudoFilePut; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; use function array_filter; use function array_map; @@ -30,7 +30,7 @@ class RemoveIniEntryWithFileGetContents implements RemoveIniEntry { /** @return list Returns a list of INI files that were updated to remove the extension */ - public function __invoke(Package $package, TargetPlatform $targetPlatform, OutputInterface $output): array + public function __invoke(Package $package, TargetPlatform $targetPlatform, IOInterface $io): array { $allIniFiles = []; @@ -76,7 +76,7 @@ static function (string $path) use ($additionalIniDirectory): bool { $updatedIniFiles = []; array_walk( $allIniFiles, - static function (string $iniFile) use (&$updatedIniFiles, $regex, $package, $output): void { + static function (string $iniFile) use (&$updatedIniFiles, $regex, $package, $io): void { $currentContent = file_get_contents($iniFile); if ($currentContent === false || $currentContent === '') { @@ -96,7 +96,7 @@ static function (string $iniFile) use (&$updatedIniFiles, $regex, $package, $out try { SudoFilePut::contents($iniFile, $replacedContent); } catch (FailedToWriteFile) { - $output->writeln(sprintf( + $io->write(sprintf( 'Failed to remove extension "%s" from INI file "%s"', $package->extensionName()->name(), $iniFile, diff --git a/src/Installing/Ini/SetupIniApproach.php b/src/Installing/Ini/SetupIniApproach.php index 0e003e0d..9c535145 100644 --- a/src/Installing/Ini/SetupIniApproach.php +++ b/src/Installing/Ini/SetupIniApproach.php @@ -4,10 +4,10 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ interface SetupIniApproach @@ -26,6 +26,6 @@ public function setup( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, ): bool; } diff --git a/src/Installing/Ini/StandardAdditionalPhpIniDirectory.php b/src/Installing/Ini/StandardAdditionalPhpIniDirectory.php index 7e668ea5..d5dbd360 100644 --- a/src/Installing/Ini/StandardAdditionalPhpIniDirectory.php +++ b/src/Installing/Ini/StandardAdditionalPhpIniDirectory.php @@ -4,13 +4,13 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\File\Sudo; use Php\Pie\File\SudoCreate; use Php\Pie\File\SudoUnlink; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; use function file_exists; use function is_writable; @@ -36,7 +36,7 @@ public function setup( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, ): bool { $additionalIniFilesPath = $targetPlatform->phpBinaryPath->additionalIniDirectory(); @@ -46,24 +46,24 @@ public function setup( } if (! file_exists($additionalIniFilesPath)) { - $output->writeln( + $io->write( sprintf( 'PHP is configured to use additional INI file path %s, but it did not exist.', $additionalIniFilesPath, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; } if (! is_writable($additionalIniFilesPath) && ! Sudo::exists()) { - $output->writeln( + $io->write( sprintf( 'PHP is configured to use additional INI file path %s, but it was not writable by PIE.', $additionalIniFilesPath, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); return false; @@ -79,12 +79,12 @@ public function setup( $pieCreatedTheIniFile = false; if (! file_exists($expectedIniFile)) { - $output->writeln( + $io->write( sprintf( 'Creating new INI file based on extension priority: %s', $expectedIniFile, ), - OutputInterface::VERBOSITY_VERY_VERBOSE, + verbosity: IOInterface::VERY_VERBOSE, ); $pieCreatedTheIniFile = true; SudoCreate::file($expectedIniFile); @@ -94,7 +94,7 @@ public function setup( $expectedIniFile, $targetPlatform, $downloadedPackage, - $output, + $io, null, ); diff --git a/src/Installing/Ini/StandardSinglePhpIni.php b/src/Installing/Ini/StandardSinglePhpIni.php index decaf6da..a0e0bae6 100644 --- a/src/Installing/Ini/StandardSinglePhpIni.php +++ b/src/Installing/Ini/StandardSinglePhpIni.php @@ -4,10 +4,10 @@ namespace Php\Pie\Installing\Ini; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ final class StandardSinglePhpIni implements SetupIniApproach @@ -26,7 +26,7 @@ public function setup( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, ): bool { $ini = $targetPlatform->phpBinaryPath->loadedIniConfigurationFile(); @@ -39,7 +39,7 @@ public function setup( $ini, $targetPlatform, $downloadedPackage, - $output, + $io, null, ); } diff --git a/src/Installing/Install.php b/src/Installing/Install.php index 0e1b3fee..ebdda216 100644 --- a/src/Installing/Install.php +++ b/src/Installing/Install.php @@ -4,10 +4,10 @@ namespace Php\Pie\Installing; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\Platform\TargetPlatform; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ interface Install @@ -19,7 +19,7 @@ interface Install public function __invoke( DownloadedPackage $downloadedPackage, TargetPlatform $targetPlatform, - OutputInterface $output, + IOInterface $io, bool $attemptToSetupIniFile, ): BinaryFile; } diff --git a/src/Installing/InstallForPhpProject/ComposerFactoryForProject.php b/src/Installing/InstallForPhpProject/ComposerFactoryForProject.php index 79fd486b..4f198db2 100644 --- a/src/Installing/InstallForPhpProject/ComposerFactoryForProject.php +++ b/src/Installing/InstallForPhpProject/ComposerFactoryForProject.php @@ -6,32 +6,25 @@ use Composer\Composer; use Composer\Factory as ComposerFactory; -use Composer\IO\ConsoleIO; +use Composer\IO\IOInterface; use Composer\Package\RootPackageInterface; -use Symfony\Component\Console\Helper\HelperSet; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ class ComposerFactoryForProject { private Composer|null $memoizedComposer = null; - public function composer(InputInterface $input, OutputInterface $output): Composer + public function composer(IOInterface $io): Composer { if ($this->memoizedComposer === null) { - $this->memoizedComposer = ComposerFactory::create(new ConsoleIO( - $input, - $output, - new HelperSet([]), - )); + $this->memoizedComposer = ComposerFactory::create($io); } return $this->memoizedComposer; } - public function rootPackage(InputInterface $input, OutputInterface $output): RootPackageInterface + public function rootPackage(IOInterface $io): RootPackageInterface { - return $this->composer($input, $output)->getPackage(); + return $this->composer($io)->getPackage(); } } diff --git a/src/Installing/InstallForPhpProject/InstallPiePackageFromPath.php b/src/Installing/InstallForPhpProject/InstallPiePackageFromPath.php index 209da08d..3e8771fc 100644 --- a/src/Installing/InstallForPhpProject/InstallPiePackageFromPath.php +++ b/src/Installing/InstallForPhpProject/InstallPiePackageFromPath.php @@ -4,12 +4,12 @@ namespace Php\Pie\Installing\InstallForPhpProject; +use Composer\IO\IOInterface; use Composer\Package\RootPackageInterface; use Php\Pie\Command\InvokeSubCommand; use Php\Pie\ComposerIntegration\PieJsonEditor; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\OutputInterface; use function sprintf; @@ -21,9 +21,15 @@ public function __construct(private readonly InvokeSubCommand $invokeSubCommand) } /** @param non-empty-string $piePackagePath */ - public function __invoke(Command $invokeContext, string $piePackagePath, RootPackageInterface $pieRootPackage, PieJsonEditor $pieJsonEditor, InputInterface $input, OutputInterface $output): int - { - $output->writeln(sprintf('Installing PIE extension from %s', $piePackagePath)); + public function __invoke( + Command $invokeContext, + string $piePackagePath, + RootPackageInterface $pieRootPackage, + PieJsonEditor $pieJsonEditor, + InputInterface $input, + IOInterface $io, + ): int { + $io->write(sprintf('Installing PIE extension from %s', $piePackagePath)); $pieJsonEditor ->ensureExists() ->addRepository('path', $piePackagePath); @@ -36,15 +42,14 @@ public function __invoke(Command $invokeContext, string $piePackagePath, RootPac 'requested-package-and-version' => $pieRootPackage->getName() . ':*@dev', ], $input, - $output, ); } finally { - $output->writeln( + $io->write( sprintf( 'Removing temporary path repository: %s', $piePackagePath, ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); $pieJsonEditor->removeRepository($piePackagePath); } diff --git a/src/Installing/InstallForPhpProject/InstallSelectedPackage.php b/src/Installing/InstallForPhpProject/InstallSelectedPackage.php index 62022f69..c93b54c0 100644 --- a/src/Installing/InstallForPhpProject/InstallSelectedPackage.php +++ b/src/Installing/InstallForPhpProject/InstallSelectedPackage.php @@ -4,12 +4,11 @@ namespace Php\Pie\Installing\InstallForPhpProject; +use Composer\IO\IOInterface; use Php\Pie\Command\CommandHelper; use Php\Pie\File\FullPathToSelf; use Php\Pie\Util\Process; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\ConsoleOutputInterface; -use Symfony\Component\Console\Output\OutputInterface; use function array_filter; use function array_walk; @@ -25,7 +24,7 @@ public function __construct(private readonly FullPathToSelf $fullPathToSelf) { } - public function withPieCli(string $selectedPackage, InputInterface $input, OutputInterface $output): void + public function withPieCli(string $selectedPackage, InputInterface $input, IOInterface $io): void { $process = [ ($this->fullPathToSelf)(), @@ -62,14 +61,14 @@ static function (string $value, string $key) use (&$process): void { $process, getcwd(), null, - static function (string $outOrErr, string $message) use ($output): void { - if ($output instanceof ConsoleOutputInterface && $outOrErr === \Symfony\Component\Process\Process::ERR) { - $output->getErrorOutput()->write(' > ' . $message); + static function (string $outOrErr, string $message) use ($io): void { + if ($outOrErr === \Symfony\Component\Process\Process::ERR) { + $io->writeError(' > ' . $message); return; } - $output->write(' > ' . $message); + $io->write(' > ' . $message); }, ); } diff --git a/src/Installing/SetupIniFile.php b/src/Installing/SetupIniFile.php index d7978a51..f69f98f2 100644 --- a/src/Installing/SetupIniFile.php +++ b/src/Installing/SetupIniFile.php @@ -4,13 +4,13 @@ namespace Php\Pie\Installing; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\ExtensionType; use Php\Pie\File\BinaryFile; use Php\Pie\Installing\Ini\SetupIniApproach; use Php\Pie\Platform\TargetPlatform; use Php\Pie\Util\Emoji; -use Symfony\Component\Console\Output\OutputInterface; use function sprintf; @@ -25,26 +25,26 @@ public function __invoke( TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, BinaryFile $binaryFile, - OutputInterface $output, + IOInterface $io, bool $attemptToSetupIniFile, ): void { if ( $attemptToSetupIniFile && $this->setupIniApproach->canBeUsed($targetPlatform) - && $this->setupIniApproach->setup($targetPlatform, $downloadedPackage, $binaryFile, $output) + && $this->setupIniApproach->setup($targetPlatform, $downloadedPackage, $binaryFile, $io) ) { - $output->writeln(sprintf( + $io->write(sprintf( '%s Extension is enabled and loaded in %s', Emoji::GREEN_CHECKMARK, $targetPlatform->phpBinaryPath->phpBinaryPath, )); } else { if (! $attemptToSetupIniFile) { - $output->writeln('Automatic extension enabling was skipped.', OutputInterface::VERBOSITY_VERBOSE); + $io->write('Automatic extension enabling was skipped.', verbosity: IOInterface::VERY_VERBOSE); } - $output->writeln(sprintf('%s Extension has NOT been automatically enabled.', Emoji::WARNING)); - $output->writeln(sprintf( + $io->write(sprintf('%s Extension has NOT been automatically enabled.', Emoji::WARNING)); + $io->write(sprintf( 'You must now add "%s=%s" to your php.ini', $downloadedPackage->package->extensionType() === ExtensionType::PhpModule ? 'extension' : 'zend_extension', $downloadedPackage->package->extensionName()->name(), diff --git a/src/Installing/UnixInstall.php b/src/Installing/UnixInstall.php index 70f33b63..02e32489 100644 --- a/src/Installing/UnixInstall.php +++ b/src/Installing/UnixInstall.php @@ -4,13 +4,13 @@ namespace Php\Pie\Installing; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\File\Sudo; use Php\Pie\Platform\TargetPlatform; use Php\Pie\Util\Process; use RuntimeException; -use Symfony\Component\Console\Output\OutputInterface; use function array_unshift; use function file_exists; @@ -29,7 +29,7 @@ public function __construct(private readonly SetupIniFile $setupIniFile) public function __invoke( DownloadedPackage $downloadedPackage, TargetPlatform $targetPlatform, - OutputInterface $output, + IOInterface $io, bool $attemptToSetupIniFile, ): BinaryFile { $targetExtensionPath = $targetPlatform->phpBinaryPath->extensionPath(); @@ -51,7 +51,7 @@ public function __invoke( ) && Sudo::exists() ) { - $output->writeln(sprintf( + $io->write(sprintf( 'Cannot write to %s, so using sudo to elevate privileges.', $targetExtensionPath, )); @@ -64,15 +64,13 @@ public function __invoke( self::MAKE_INSTALL_TIMEOUT_SECS, ); - if ($output->isVeryVerbose()) { - $output->writeln($makeInstallOutput); - } + $io->write($makeInstallOutput, verbosity: IOInterface::VERY_VERBOSE); if (! file_exists($expectedSharedObjectLocation)) { throw new RuntimeException('Install failed, ' . $expectedSharedObjectLocation . ' was not installed.'); } - $output->writeln('Install complete: ' . $expectedSharedObjectLocation); + $io->write('Install complete: ' . $expectedSharedObjectLocation); $binaryFile = BinaryFile::fromFileWithSha256Checksum($expectedSharedObjectLocation); @@ -80,7 +78,7 @@ public function __invoke( $targetPlatform, $downloadedPackage, $binaryFile, - $output, + $io, $attemptToSetupIniFile, ); diff --git a/src/Installing/WindowsInstall.php b/src/Installing/WindowsInstall.php index a97f0f54..97eae97e 100644 --- a/src/Installing/WindowsInstall.php +++ b/src/Installing/WindowsInstall.php @@ -4,6 +4,7 @@ namespace Php\Pie\Installing; +use Composer\IO\IOInterface; use Php\Pie\Downloading\DownloadedPackage; use Php\Pie\File\BinaryFile; use Php\Pie\File\WindowsDelete; @@ -13,7 +14,6 @@ use RecursiveIteratorIterator; use RuntimeException; use SplFileInfo; -use Symfony\Component\Console\Output\OutputInterface; use function assert; use function copy; @@ -37,7 +37,7 @@ public function __construct(private readonly SetupIniFile $setupIniFile) public function __invoke( DownloadedPackage $downloadedPackage, TargetPlatform $targetPlatform, - OutputInterface $output, + IOInterface $io, bool $attemptToSetupIniFile, ): BinaryFile { $extractedSourcePath = $downloadedPackage->extractedSourcePath; @@ -46,11 +46,11 @@ public function __invoke( assert($sourcePdbName !== ''); $destinationDllName = $this->copyExtensionDll($targetPlatform, $downloadedPackage, $sourceDllName); - $output->writeln('Copied DLL to: ' . $destinationDllName); + $io->write('Copied DLL to: ' . $destinationDllName); $destinationPdbName = $this->copyExtensionPdb($targetPlatform, $downloadedPackage, $sourcePdbName, $destinationDllName); if ($destinationPdbName !== null) { - $output->writeln('Copied PDB to: ' . $destinationPdbName); + $io->write('Copied PDB to: ' . $destinationPdbName); } foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($extractedSourcePath)) as $file) { @@ -69,13 +69,13 @@ public function __invoke( $destinationExtraDll = $this->copyDependencyDll($targetPlatform, $file); if ($destinationExtraDll !== null) { - $output->writeln('Copied extra DLL: ' . $destinationExtraDll); + $io->write('Copied extra DLL: ' . $destinationExtraDll); continue; } $destinationPathname = $this->copyExtraFile($targetPlatform, $downloadedPackage, $file); - $output->writeln('Copied extras: ' . $destinationPathname); + $io->write('Copied extras: ' . $destinationPathname); } $binaryFile = BinaryFile::fromFileWithSha256Checksum($destinationDllName); @@ -84,7 +84,7 @@ public function __invoke( $targetPlatform, $downloadedPackage, $binaryFile, - $output, + $io, $attemptToSetupIniFile, ); diff --git a/src/Platform/TargetPhp/PhpBinaryPath.php b/src/Platform/TargetPhp/PhpBinaryPath.php index d08db710..61216ccc 100644 --- a/src/Platform/TargetPhp/PhpBinaryPath.php +++ b/src/Platform/TargetPhp/PhpBinaryPath.php @@ -4,6 +4,7 @@ namespace Php\Pie\Platform\TargetPhp; +use Composer\IO\IOInterface; use Composer\Semver\VersionParser; use Composer\Util\Platform; use Php\Pie\ExtensionName; @@ -12,7 +13,6 @@ use Php\Pie\Platform\OperatingSystemFamily; use Php\Pie\Util\Process; use RuntimeException; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\PhpExecutableFinder; use Webmozart\Assert\Assert; @@ -124,7 +124,7 @@ public function extensionPath(): string throw new RuntimeException('Could not determine extension path for ' . $this->phpBinaryPath); } - public function assertExtensionIsLoadedInRuntime(ExtensionName $extension, OutputInterface|null $output = null): void + public function assertExtensionIsLoadedInRuntime(ExtensionName $extension, IOInterface|null $io = null): void { if (! in_array(strtolower($extension->name()), array_map('strtolower', array_keys($this->extensions())))) { throw Exception\ExtensionIsNotLoaded::fromExpectedExtension( @@ -133,16 +133,16 @@ public function assertExtensionIsLoadedInRuntime(ExtensionName $extension, Outpu ); } - if ($output === null) { + if ($io === null) { return; } - $output->writeln( + $io->write( sprintf( 'Successfully asserted that extension %s is loaded in runtime.', $extension->name(), ), - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); } @@ -252,13 +252,13 @@ public function operatingSystem(): OperatingSystem public function operatingSystemFamily(): OperatingSystemFamily { - $output = self::cleanWarningAndDeprecationsFromOutput(Process::run([ - $this->phpBinaryPath, - '-r', - 'echo PHP_OS_FAMILY;', - ])); - - $osFamily = OperatingSystemFamily::tryFrom(strtolower(trim($output))); + $osFamily = OperatingSystemFamily::tryFrom(strtolower(trim( + self::cleanWarningAndDeprecationsFromOutput(Process::run([ + $this->phpBinaryPath, + '-r', + 'echo PHP_OS_FAMILY;', + ])), + ))); Assert::notNull($osFamily, 'Could not determine operating system family'); return $osFamily; diff --git a/src/SelfManage/Verify/FallbackVerificationUsingOpenSsl.php b/src/SelfManage/Verify/FallbackVerificationUsingOpenSsl.php index ff2af594..3c2a80ae 100644 --- a/src/SelfManage/Verify/FallbackVerificationUsingOpenSsl.php +++ b/src/SelfManage/Verify/FallbackVerificationUsingOpenSsl.php @@ -4,10 +4,10 @@ namespace Php\Pie\SelfManage\Verify; +use Composer\IO\IOInterface; use Php\Pie\File\BinaryFile; use Php\Pie\SelfManage\Update\ReleaseMetadata; use Php\Pie\Util\Emoji; -use Symfony\Component\Console\Output\OutputInterface; use ThePhpFoundation\Attestation\FilenameWithChecksum; use ThePhpFoundation\Attestation\FulcioSigstoreOidExtensions; use ThePhpFoundation\Attestation\Verification\Exception\FailedToVerifyArtifact; @@ -34,11 +34,11 @@ public function __construct( ) { } - public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, OutputInterface $output): void + public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, IOInterface $io): void { - $output->writeln( + $io->write( 'Falling back to basic verification. To use full verification, install the `gh` CLI tool.', - OutputInterface::VERBOSITY_VERBOSE, + verbosity: IOInterface::VERBOSE, ); try { @@ -53,7 +53,7 @@ public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilenam throw FailedToVerifyRelease::fromAttestationException($failedToVerifyArtifact); } - $output->writeln(sprintf( + $io->write(sprintf( '%s Verified the new PIE version (using fallback verification)', Emoji::GREEN_CHECKMARK, )); diff --git a/src/SelfManage/Verify/GithubCliAttestationVerification.php b/src/SelfManage/Verify/GithubCliAttestationVerification.php index 457b994d..f3803929 100644 --- a/src/SelfManage/Verify/GithubCliAttestationVerification.php +++ b/src/SelfManage/Verify/GithubCliAttestationVerification.php @@ -4,11 +4,11 @@ namespace Php\Pie\SelfManage\Verify; +use Composer\IO\IOInterface; use Php\Pie\File\BinaryFile; use Php\Pie\SelfManage\Update\ReleaseMetadata; use Php\Pie\Util\Emoji; use Php\Pie\Util\Process; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Exception\ProcessFailedException; use Symfony\Component\Process\ExecutableFinder; @@ -27,7 +27,7 @@ public function __construct(private readonly ExecutableFinder $executableFinder) { } - public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, OutputInterface $output): void + public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, IOInterface $io): void { $gh = $this->executableFinder->find(self::GH_CLI_NAME); @@ -54,7 +54,10 @@ public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilenam $pharFilename->filePath, ]; - $output->writeln('Verifying using: ' . implode(' ', $verificationCommand), OutputInterface::VERBOSITY_VERBOSE); + $io->write( + 'Verifying using: ' . implode(' ', $verificationCommand), + verbosity: IOInterface::VERBOSE, + ); try { Process::run($verificationCommand, null, self::GH_VERIFICATION_TIMEOUT); @@ -62,6 +65,6 @@ public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilenam throw FailedToVerifyRelease::fromGhCliFailure($releaseMetadata, $processFailedException); } - $output->writeln(sprintf('%s Verified the new PIE version', Emoji::GREEN_CHECKMARK)); + $io->write(sprintf('%s Verified the new PIE version', Emoji::GREEN_CHECKMARK)); } } diff --git a/src/SelfManage/Verify/VerifyPiePhar.php b/src/SelfManage/Verify/VerifyPiePhar.php index 1d3ac05a..cae1bb65 100644 --- a/src/SelfManage/Verify/VerifyPiePhar.php +++ b/src/SelfManage/Verify/VerifyPiePhar.php @@ -4,13 +4,13 @@ namespace Php\Pie\SelfManage\Verify; +use Composer\IO\IOInterface; use Php\Pie\File\BinaryFile; use Php\Pie\SelfManage\Update\ReleaseMetadata; -use Symfony\Component\Console\Output\OutputInterface; /** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */ interface VerifyPiePhar { /** @throws FailedToVerifyRelease */ - public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, OutputInterface $output): void; + public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, IOInterface $io): void; } diff --git a/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php b/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php index fbf3f24a..6ea97a88 100644 --- a/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php +++ b/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php @@ -4,9 +4,9 @@ namespace Php\Pie\SelfManage\Verify; +use Composer\IO\IOInterface; use Php\Pie\File\BinaryFile; use Php\Pie\SelfManage\Update\ReleaseMetadata; -use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\ExecutableFinder; use ThePhpFoundation\Attestation\Verification\VerifyAttestationWithOpenSsl; @@ -29,18 +29,18 @@ public static function factory(): self ); } - public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, OutputInterface $output): void + public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilename, IOInterface $io): void { try { - $this->githubCliVerification->verify($releaseMetadata, $pharFilename, $output); + $this->githubCliVerification->verify($releaseMetadata, $pharFilename, $io); } catch (GithubCliNotAvailable $githubCliNotAvailable) { - $output->writeln($githubCliNotAvailable->getMessage(), OutputInterface::VERBOSITY_VERBOSE); + $io->write($githubCliNotAvailable->getMessage(), verbosity: IOInterface::VERBOSE); if (! extension_loaded('openssl')) { throw FailedToVerifyRelease::fromNoOpenssl(); } - $this->fallbackVerification->verify($releaseMetadata, $pharFilename, $output); + $this->fallbackVerification->verify($releaseMetadata, $pharFilename, $io); } } } diff --git a/test/integration/Building/UnixBuildTest.php b/test/integration/Building/UnixBuildTest.php index 1d6daa6c..a8436324 100644 --- a/test/integration/Building/UnixBuildTest.php +++ b/test/integration/Building/UnixBuildTest.php @@ -4,6 +4,7 @@ namespace Php\PieIntegrationTest\Building; +use Composer\IO\BufferIO; use Composer\Package\CompletePackage; use Composer\Util\Platform; use Php\Pie\Building\ExtensionBinaryNotFound; @@ -16,7 +17,6 @@ use Php\Pie\Platform\TargetPlatform; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\Process; @@ -33,7 +33,7 @@ public function testUnixBuildCanBuildExtension(): void self::markTestSkipped('Unix build test cannot be run on Windows'); } - $output = new BufferedOutput(); + $output = new BufferIO(); $downloadedPackage = DownloadedPackage::fromPackageAndExtractedPath( new Package( @@ -58,7 +58,7 @@ public function testUnixBuildCanBuildExtension(): void self::assertNotEmpty($builtBinary); - $outputString = $output->fetch(); + $outputString = $output->getOutput(); self::assertStringContainsString('phpize complete.', $outputString); self::assertStringContainsString('Configure complete with options: --enable-pie_test_ext', $outputString); @@ -82,7 +82,7 @@ public function testUnixBuildWillThrowExceptionWhenExpectedBinaryNameMismatches( self::markTestSkipped('Unix build test cannot be run on Windows'); } - $output = new BufferedOutput(); + $output = new BufferIO(); $downloadedPackage = DownloadedPackage::fromPackageAndExtractedPath( new Package( @@ -119,7 +119,7 @@ public function testUnixBuildCanBuildExtensionWithBuildPath(): void self::markTestSkipped('Unix build test cannot be run on Windows'); } - $output = new BufferedOutput(); + $output = new BufferIO(); $composerPackage = $this->createMock(CompletePackage::class); $composerPackage->method('getPrettyName')->willReturn('myvendor/pie_test_ext'); @@ -143,7 +143,7 @@ public function testUnixBuildCanBuildExtensionWithBuildPath(): void self::assertNotEmpty($builtBinary); - $outputString = $output->fetch(); + $outputString = $output->getOutput(); self::assertStringContainsString('phpize complete.', $outputString); self::assertStringContainsString('Configure complete with options: --enable-pie_test_ext', $outputString); @@ -170,8 +170,7 @@ public function testCleanupDoesNotCleanWhenConfigureIsMissing(): void (new Process(['phpize', '--clean'], self::TEST_EXTENSION_PATH))->mustRun(); self::assertFileDoesNotExist(self::TEST_EXTENSION_PATH . '/configure'); - $output = new BufferedOutput(); - $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $output = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $downloadedPackage = DownloadedPackage::fromPackageAndExtractedPath( new Package( @@ -194,7 +193,7 @@ public function testCleanupDoesNotCleanWhenConfigureIsMissing(): void null, ); - $outputString = $output->fetch(); + $outputString = $output->getOutput(); self::assertStringContainsString('Skipping phpize --clean, configure does not exist', $outputString); self::assertStringNotContainsString('Build files cleaned up', $outputString); } @@ -208,8 +207,7 @@ public function testVerboseOutputShowsCleanupMessages(): void (new Process(['phpize'], self::TEST_EXTENSION_PATH))->mustRun(); self::assertFileExists(self::TEST_EXTENSION_PATH . '/configure'); - $output = new BufferedOutput(); - $output->setVerbosity(OutputInterface::VERBOSITY_VERBOSE); + $output = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $downloadedPackage = DownloadedPackage::fromPackageAndExtractedPath( new Package( @@ -232,7 +230,7 @@ public function testVerboseOutputShowsCleanupMessages(): void null, ); - $outputString = $output->fetch(); + $outputString = $output->getOutput(); self::assertStringContainsString('Running phpize --clean step', $outputString); self::assertStringContainsString('Build files cleaned up', $outputString); } diff --git a/test/integration/Command/BuildCommandTest.php b/test/integration/Command/BuildCommandTest.php index ab382a6f..7e8d7010 100644 --- a/test/integration/Command/BuildCommandTest.php +++ b/test/integration/Command/BuildCommandTest.php @@ -9,7 +9,6 @@ use Php\Pie\Container; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Tester\CommandTester; #[CoversClass(BuildCommand::class)] class BuildCommandTest extends TestCase @@ -20,7 +19,7 @@ class BuildCommandTest extends TestCase public function setUp(): void { - $this->commandTester = new CommandTester(Container::factory()->get(BuildCommand::class)); + $this->commandTester = new CommandTester(Container::testFactory()->get(BuildCommand::class)); } public function testBuildCommandWillBuildTheExtension(): void diff --git a/test/integration/Command/CommandTester.php b/test/integration/Command/CommandTester.php new file mode 100644 index 00000000..d52ff63e --- /dev/null +++ b/test/integration/Command/CommandTester.php @@ -0,0 +1,39 @@ + $input + * @param array $options + * + * @inheritDoc + */ + public function execute(array $input, array $options = []): int + { + $buffer = Container::testBuffer(); + $output = (new ReflectionProperty(BufferIO::class, 'output'))->getValue($buffer); + assert($output instanceof StreamOutput); + $stream = $output->getStream(); + ftruncate($stream, 0); + + return parent::execute($input, $options); + } + + public function getDisplay(bool $normalize = false): string + { + return Container::testBuffer()->getOutput(); + } +} diff --git a/test/integration/Command/DownloadCommandTest.php b/test/integration/Command/DownloadCommandTest.php index 8f0116da..13da63e7 100644 --- a/test/integration/Command/DownloadCommandTest.php +++ b/test/integration/Command/DownloadCommandTest.php @@ -12,7 +12,6 @@ use PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily; use PHPUnit\Framework\Attributes\RequiresPhp; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Tester\CommandTester; use function array_combine; use function array_map; @@ -31,7 +30,7 @@ class DownloadCommandTest extends TestCase public function setUp(): void { - $this->commandTester = new CommandTester(Container::factory()->get(DownloadCommand::class)); + $this->commandTester = new CommandTester(Container::testFactory()->get(DownloadCommand::class)); } /** diff --git a/test/integration/Command/InfoCommandTest.php b/test/integration/Command/InfoCommandTest.php index 2db28b5f..a0f520d8 100644 --- a/test/integration/Command/InfoCommandTest.php +++ b/test/integration/Command/InfoCommandTest.php @@ -8,7 +8,6 @@ use Php\Pie\Container; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Tester\CommandTester; #[CoversClass(InfoCommand::class)] final class InfoCommandTest extends TestCase @@ -17,7 +16,7 @@ final class InfoCommandTest extends TestCase public function setUp(): void { - $this->commandTester = new CommandTester(Container::factory()->get(InfoCommand::class)); + $this->commandTester = new CommandTester(Container::testFactory()->get(InfoCommand::class)); } public function testInfoCommandDisplaysInformation(): void diff --git a/test/integration/Command/InstallCommandTest.php b/test/integration/Command/InstallCommandTest.php index 00eeda71..531c8cd8 100644 --- a/test/integration/Command/InstallCommandTest.php +++ b/test/integration/Command/InstallCommandTest.php @@ -12,7 +12,6 @@ use PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily; use PHPUnit\Framework\TestCase; use Symfony\Component\Console\Output\BufferedOutput; -use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Process\Process; use function array_combine; @@ -37,7 +36,7 @@ class InstallCommandTest extends TestCase public function setUp(): void { - $this->commandTester = new CommandTester(Container::factory()->get(InstallCommand::class)); + $this->commandTester = new CommandTester(Container::testFactory()->get(InstallCommand::class)); } /** @return array */ diff --git a/test/integration/Command/InstallExtensionsForProjectCommandTest.php b/test/integration/Command/InstallExtensionsForProjectCommandTest.php index e05f5948..09522955 100644 --- a/test/integration/Command/InstallExtensionsForProjectCommandTest.php +++ b/test/integration/Command/InstallExtensionsForProjectCommandTest.php @@ -5,6 +5,7 @@ namespace Php\PieIntegrationTest\Command; use Composer\Composer; +use Composer\IO\IOInterface; use Composer\Package\Link; use Composer\Package\RootPackage; use Composer\Repository\InstalledArrayRepository; @@ -15,6 +16,7 @@ use Php\Pie\ComposerIntegration\MinimalHelperSet; use Php\Pie\ComposerIntegration\PieJsonEditor; use Php\Pie\ComposerIntegration\QuieterConsoleIO; +use Php\Pie\Container; use Php\Pie\ExtensionType; use Php\Pie\Installing\InstallForPhpProject\ComposerFactoryForProject; use Php\Pie\Installing\InstallForPhpProject\DetermineExtensionsRequired; @@ -32,8 +34,6 @@ use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\BufferedOutput; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Tester\CommandTester; use function getcwd; @@ -77,6 +77,7 @@ function (string $service): mixed { $this->installPiePackage = $this->createMock(InstallPiePackageFromPath::class); $this->questionHelper = $this->createMock(QuestionHelper::class); + Container::testFactory(); $cmd = new InstallExtensionsForProjectCommand( $this->composerFactoryForProject, new DetermineExtensionsRequired(), @@ -85,6 +86,7 @@ function (string $service): mixed { $this->installSelectedPackage, $this->installPiePackage, $container, + Container::testBuffer(), ); $cmd->setHelperSet(new HelperSet([ 'question' => $this->questionHelper, @@ -200,7 +202,7 @@ public function testInstallingExtensionsForPieProject(): void $rootPackage, self::isInstanceOf(PieJsonEditor::class), self::isInstanceOf(InputInterface::class), - self::isInstanceOf(OutputInterface::class), + self::isInstanceOf(IOInterface::class), ) ->willReturn(Command::SUCCESS); diff --git a/test/integration/Command/RepositoryManagementCommandsTest.php b/test/integration/Command/RepositoryManagementCommandsTest.php index 77ab6c56..b93c411e 100644 --- a/test/integration/Command/RepositoryManagementCommandsTest.php +++ b/test/integration/Command/RepositoryManagementCommandsTest.php @@ -10,7 +10,6 @@ use Php\Pie\Container; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Tester\CommandTester; use function array_filter; use function array_map; @@ -36,9 +35,9 @@ final class RepositoryManagementCommandsTest extends TestCase public function setUp(): void { - $this->listCommand = new CommandTester(Container::factory()->get(RepositoryListCommand::class)); - $this->addCommand = new CommandTester(Container::factory()->get(RepositoryAddCommand::class)); - $this->removeCommand = new CommandTester(Container::factory()->get(RepositoryRemoveCommand::class)); + $this->listCommand = new CommandTester(Container::testFactory()->get(RepositoryListCommand::class)); + $this->addCommand = new CommandTester(Container::testFactory()->get(RepositoryAddCommand::class)); + $this->removeCommand = new CommandTester(Container::testFactory()->get(RepositoryRemoveCommand::class)); $this->addCommand->execute([ 'type' => 'composer', diff --git a/test/integration/Command/ShowCommandTest.php b/test/integration/Command/ShowCommandTest.php index 7fad6088..c1a4948f 100644 --- a/test/integration/Command/ShowCommandTest.php +++ b/test/integration/Command/ShowCommandTest.php @@ -13,7 +13,6 @@ use Php\Pie\Util\Process; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\Process\Exception\ProcessFailedException; use Webmozart\Assert\Assert; @@ -32,7 +31,7 @@ final class ShowCommandTest extends TestCase public function setUp(): void { - $this->commandTester = new CommandTester(Container::factory()->get(ShowCommand::class)); + $this->commandTester = new CommandTester(Container::testFactory()->get(ShowCommand::class)); } public function testExecute(): void @@ -64,7 +63,7 @@ public function testExecuteWithAvailableConstrainedUpdates(): void self::markTestSkipped('This test can only run on systems with php-config'); } - $installCommand = new CommandTester(Container::factory()->get(InstallCommand::class)); + $installCommand = new CommandTester(Container::testFactory()->get(InstallCommand::class)); $installCommand->execute([ 'requested-package-and-version' => self::TEST_PACKAGE . ':2.0.2', '--with-php-config' => $phpConfig, @@ -111,7 +110,7 @@ public function testExecuteWithAvailableUnconstrainedUpdates(): void self::markTestSkipped('This test can only run on systems with php-config'); } - $installCommand = new CommandTester(Container::factory()->get(InstallCommand::class)); + $installCommand = new CommandTester(Container::testFactory()->get(InstallCommand::class)); $installCommand->execute([ 'requested-package-and-version' => self::TEST_PACKAGE . ':2.0.2', '--with-php-config' => $phpConfig, @@ -158,7 +157,7 @@ public function testExecuteWithOnlyUnconstrainedUpdates(): void self::markTestSkipped('This test can only run on systems with php-config'); } - $installCommand = new CommandTester(Container::factory()->get(InstallCommand::class)); + $installCommand = new CommandTester(Container::testFactory()->get(InstallCommand::class)); $installCommand->execute([ 'requested-package-and-version' => self::TEST_PACKAGE . ':2.0.2', '--with-php-config' => $phpConfig, diff --git a/test/integration/DependencyResolver/ResolveDependencyWithComposerTest.php b/test/integration/DependencyResolver/ResolveDependencyWithComposerTest.php index 4c03c8c9..0fcfbf51 100644 --- a/test/integration/DependencyResolver/ResolveDependencyWithComposerTest.php +++ b/test/integration/DependencyResolver/ResolveDependencyWithComposerTest.php @@ -4,6 +4,7 @@ namespace Php\PieIntegrationTest\DependencyResolver; +use Composer\IO\IOInterface; use Php\Pie\ComposerIntegration\PieComposerFactory; use Php\Pie\ComposerIntegration\PieComposerRequest; use Php\Pie\ComposerIntegration\PieOperation; @@ -16,7 +17,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\OutputInterface; use function array_combine; use function array_map; @@ -81,7 +81,7 @@ public function testDependenciesAreResolvedToExpectedVersions( PieComposerFactory::createPieComposer( $container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $targetPlatform, $requestedPackageAndVersion, PieOperation::Resolve, diff --git a/test/integration/Installing/UnixInstallTest.php b/test/integration/Installing/UnixInstallTest.php index d8c8cedf..7709cddf 100644 --- a/test/integration/Installing/UnixInstallTest.php +++ b/test/integration/Installing/UnixInstallTest.php @@ -4,6 +4,7 @@ namespace Php\PieIntegrationTest\Installing; +use Composer\IO\BufferIO; use Composer\Package\CompletePackage; use Composer\Util\Platform; use Php\Pie\Building\UnixBuild; @@ -19,7 +20,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Process\Process; use function array_combine; @@ -72,7 +72,7 @@ public function testUnixInstallCanInstallExtension(string $phpConfig): void self::markTestSkipped('Unix build test cannot be run on Windows'); } - $output = new BufferedOutput(); + $output = new BufferIO(); $targetPlatform = TargetPlatform::fromPhpBinaryPath(PhpBinaryPath::fromPhpConfigExecutable($phpConfig), null); $extensionPath = $targetPlatform->phpBinaryPath->extensionPath(); @@ -102,7 +102,7 @@ public function testUnixInstallCanInstallExtension(string $phpConfig): void $output, true, ); - $outputString = $output->fetch(); + $outputString = $output->getOutput(); self::assertStringContainsString('Install complete: ' . $extensionPath . '/pie_test_ext.so', $outputString); self::assertStringContainsString('You must now add "extension=pie_test_ext" to your php.ini', $outputString); diff --git a/test/integration/Installing/WindowsInstallTest.php b/test/integration/Installing/WindowsInstallTest.php index 58cb0653..0f6c2fad 100644 --- a/test/integration/Installing/WindowsInstallTest.php +++ b/test/integration/Installing/WindowsInstallTest.php @@ -4,6 +4,7 @@ namespace Php\PieIntegrationTest\Installing; +use Composer\IO\BufferIO; use Composer\Package\CompletePackage; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -25,7 +26,6 @@ use RecursiveDirectoryIterator; use RecursiveIteratorIterator; use SplFileInfo; -use Symfony\Component\Console\Output\BufferedOutput; use function assert; use function dirname; @@ -56,7 +56,7 @@ public function testWindowsInstallCanInstallExtension(): void ), self::TEST_EXTENSION_PATH, ); - $output = new BufferedOutput(); + $output = new BufferIO(); $targetPlatform = new TargetPlatform( OperatingSystem::Windows, OperatingSystemFamily::Windows, @@ -74,7 +74,7 @@ public function testWindowsInstallCanInstallExtension(): void $installedDll = $installer->__invoke($downloadedPackage, $targetPlatform, $output, true); self::assertSame($extensionPath . '\php_pie_test_ext.dll', $installedDll->filePath); - $outputString = $output->fetch(); + $outputString = $output->getOutput(); self::assertStringContainsString('Copied DLL to: ' . $extensionPath . '\php_pie_test_ext.dll', $outputString); self::assertStringContainsString('You must now add "extension=pie_test_ext" to your php.ini', $outputString); diff --git a/test/unit/Command/CommandHelperTest.php b/test/unit/Command/CommandHelperTest.php index 7832d30c..98b1c743 100644 --- a/test/unit/Command/CommandHelperTest.php +++ b/test/unit/Command/CommandHelperTest.php @@ -5,6 +5,8 @@ namespace Php\PieUnitTest\Command; use Composer\Composer; +use Composer\IO\BufferIO; +use Composer\IO\NullIO; use Composer\Package\CompletePackage; use Composer\Repository\ComposerRepository; use Composer\Repository\PathRepository; @@ -25,8 +27,6 @@ use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\BufferedOutput; -use Symfony\Component\Console\Output\NullOutput; use function array_combine; use function array_map; @@ -132,13 +132,13 @@ public function testWindowsMachinesCannotUseWithPhpConfigOption(): void { $command = new Command(); $input = new ArrayInput(['--with-php-config' => 'C:\path\to\php-config']); - $output = new NullOutput(); + $io = new NullIO(); CommandHelper::configureDownloadBuildInstallOptions($command); CommandHelper::validateInput($input, $command); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The --with-php-config=/path/to/php-config cannot be used on Windows, use --with-php-path=/path/to/php instead.'); - CommandHelper::determineTargetPlatformFromInputs($input, $output); + CommandHelper::determineTargetPlatformFromInputs($input, $io); } public function testNonWindowsMachinesCannotUseWithPhpPathOption(): void @@ -149,13 +149,13 @@ public function testNonWindowsMachinesCannotUseWithPhpPathOption(): void $command = new Command(); $input = new ArrayInput(['--with-php-path' => '/usr/bin/php']); - $output = new NullOutput(); + $io = new NullIO(); CommandHelper::configureDownloadBuildInstallOptions($command); CommandHelper::validateInput($input, $command); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The --with-php-path=/path/to/php cannot be used on non-Windows, use --with-php-config=/path/to/php-config instead.'); - CommandHelper::determineTargetPlatformFromInputs($input, $output); + CommandHelper::determineTargetPlatformFromInputs($input, $io); } #[RequiresOperatingSystemFamily('Windows')] @@ -163,18 +163,18 @@ public function testWindowsMachinesCannotUseWithPhpizePathOption(): void { $command = new Command(); $input = new ArrayInput(['--with-phpize-path' => 'C:\path\to\phpize']); - $output = new NullOutput(); + $io = new NullIO(); CommandHelper::configureDownloadBuildInstallOptions($command); CommandHelper::validateInput($input, $command); $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage('The --with-phpize-path=/path/to/phpize cannot be used on Windows.'); - CommandHelper::determineTargetPlatformFromInputs($input, $output); + CommandHelper::determineTargetPlatformFromInputs($input, $io); } public function testListRepositories(): void { - $output = new BufferedOutput(); + $io = new BufferIO(); $packagistRepo = $this->createMock(ComposerRepository::class); $packagistRepo->method('getRepoConfig')->willReturn(['url' => 'https://repo.packagist.org']); @@ -202,7 +202,7 @@ public function testListRepositories(): void $composer = $this->createMock(Composer::class); $composer->method('getRepositoryManager')->willReturn($repoManager); - CommandHelper::listRepositories($composer, $output); + CommandHelper::listRepositories($composer, $io); self::assertSame( str_replace("\r\n", "\n", <<<'OUTPUT' @@ -212,7 +212,7 @@ public function testListRepositories(): void - VCS Repository (https://github.com/php/pie) - Path Repository (/path/to/repo) OUTPUT), - str_replace("\r\n", "\n", trim($output->fetch())), + str_replace("\r\n", "\n", trim($io->getOutput())), ); } } diff --git a/test/unit/ComposerIntegration/InstallAndBuildProcessTest.php b/test/unit/ComposerIntegration/InstallAndBuildProcessTest.php index 5d3f5a94..84d4f88e 100644 --- a/test/unit/ComposerIntegration/InstallAndBuildProcessTest.php +++ b/test/unit/ComposerIntegration/InstallAndBuildProcessTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\ComposerIntegration; +use Composer\IO\NullIO; use Composer\Package\CompletePackage; use Composer\PartialComposer; use Php\Pie\Building\Build; @@ -23,7 +24,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(InstallAndBuildProcess::class)] final class InstallAndBuildProcessTest extends TestCase @@ -51,10 +51,9 @@ public function setUp(): void public function testDownloadWithoutBuildAndInstall(): void { - $symfonyOutput = $this->createMock(OutputInterface::class); $composer = $this->createMock(PartialComposer::class); $composerRequest = new PieComposerRequest( - $symfonyOutput, + new NullIO(), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -93,10 +92,9 @@ public function testDownloadWithoutBuildAndInstall(): void public function testDownloadAndBuildWithoutInstall(): void { - $symfonyOutput = $this->createMock(OutputInterface::class); $composer = $this->createMock(PartialComposer::class); $composerRequest = new PieComposerRequest( - $symfonyOutput, + new NullIO(), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -138,10 +136,9 @@ public function testDownloadAndBuildWithoutInstall(): void public function testDownloadBuildAndInstall(): void { - $symfonyOutput = $this->createMock(OutputInterface::class); $composer = $this->createMock(PartialComposer::class); $composerRequest = new PieComposerRequest( - $symfonyOutput, + new NullIO(), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, diff --git a/test/unit/ComposerIntegration/InstalledJsonMetadataTest.php b/test/unit/ComposerIntegration/InstalledJsonMetadataTest.php index a4df418d..dead5ba2 100644 --- a/test/unit/ComposerIntegration/InstalledJsonMetadataTest.php +++ b/test/unit/ComposerIntegration/InstalledJsonMetadataTest.php @@ -5,6 +5,7 @@ namespace Php\PieUnitTest\ComposerIntegration; use Composer\Composer; +use Composer\IO\IOInterface; use Composer\Package\CompletePackage; use Composer\Repository\InstalledArrayRepository; use Composer\Repository\RepositoryManager; @@ -23,7 +24,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(InstalledJsonMetadata::class)] final class InstalledJsonMetadataTest extends TestCase @@ -50,7 +50,7 @@ public function testMetadataForDownloads(): void (new InstalledJsonMetadata())->addDownloadMetadata( $this->mockComposerInstalledRepositoryWith($package), new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -89,7 +89,7 @@ public function testMetadataForBuilds(): void (new InstalledJsonMetadata())->addBuildMetadata( $this->mockComposerInstalledRepositoryWith($package), new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, diff --git a/test/unit/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListenerTest.php b/test/unit/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListenerTest.php index 606ea133..5a356b8e 100644 --- a/test/unit/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListenerTest.php +++ b/test/unit/ComposerIntegration/Listeners/OverrideDownloadUrlInstallListenerTest.php @@ -28,7 +28,6 @@ use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use Psr\Container\ContainerInterface; -use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(OverrideDownloadUrlInstallListener::class)] final class OverrideDownloadUrlInstallListenerTest extends TestCase @@ -67,7 +66,7 @@ public function testEventListenerRegistration(): void $this->io, $this->container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -109,7 +108,7 @@ public function testNonInstallOperationsAreIgnored(): void $this->io, $this->container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -151,7 +150,7 @@ public function testNonCompletePackagesAreIgnored(): void $this->io, $this->container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -193,7 +192,7 @@ public function testInstallOperationsForDifferentPackagesAreIgnored(): void $this->io, $this->container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -235,7 +234,7 @@ public function testWindowsUrlInstallerDoesNotRunOnNonWindows(): void $this->io, $this->container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -289,7 +288,7 @@ public function testDistUrlIsUpdatedForWindowsInstallers(): void $this->io, $this->container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::Windows, OperatingSystemFamily::Linux, @@ -348,7 +347,7 @@ public function testDistUrlIsUpdatedForPrePackagedTgzSource(): void $this->io, $this->container, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, diff --git a/test/unit/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperationsTest.php b/test/unit/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperationsTest.php index 2ca7205a..865bae1e 100644 --- a/test/unit/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperationsTest.php +++ b/test/unit/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperationsTest.php @@ -27,7 +27,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\OutputInterface; use function array_filter; use function array_map; @@ -63,7 +62,7 @@ public function testEventListenerRegistration(): void RemoveUnrelatedInstallOperations::selfRegister( $this->composer, new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::NonWindows, OperatingSystemFamily::Linux, @@ -99,7 +98,7 @@ public function testUnrelatedInstallOperationsAreRemoved(): void (new RemoveUnrelatedInstallOperations( new PieComposerRequest( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), new TargetPlatform( OperatingSystem::Windows, OperatingSystemFamily::Linux, diff --git a/test/unit/ComposerIntegration/VendorCleanupTest.php b/test/unit/ComposerIntegration/VendorCleanupTest.php index 556b629b..0336f9b0 100644 --- a/test/unit/ComposerIntegration/VendorCleanupTest.php +++ b/test/unit/ComposerIntegration/VendorCleanupTest.php @@ -6,12 +6,12 @@ use Composer\Composer; use Composer\Config; +use Composer\IO\IOInterface; use Composer\Util\Filesystem; use Php\Pie\ComposerIntegration\VendorCleanup; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\OutputInterface; use function restore_error_handler; use function set_error_handler; @@ -24,7 +24,7 @@ final class VendorCleanupTest extends TestCase { private const VENDOR_DIR = __DIR__ . '/../../assets/vendor-cleanup-dir'; - private OutputInterface&MockObject $output; + private IOInterface&MockObject $io; private Filesystem&MockObject $filesystem; private VendorCleanup $vendorCleanup; @@ -32,10 +32,10 @@ public function setUp(): void { parent::setUp(); - $this->output = $this->createMock(OutputInterface::class); + $this->io = $this->createMock(IOInterface::class); $this->filesystem = $this->createMock(Filesystem::class); - $this->vendorCleanup = new VendorCleanup($this->output, $this->filesystem); + $this->vendorCleanup = new VendorCleanup($this->io, $this->filesystem); } private function composerWithVendorDirConfig(string $vendorDirConfig): Composer&MockObject @@ -54,12 +54,13 @@ private function composerWithVendorDirConfig(string $vendorDirConfig): Composer& public function testInvalidVendorDirectory(): void { - $this->output + $this->io ->expects(self::once()) - ->method('writeln') + ->method('write') ->with( 'Vendor directory (vendor-dir config) /path/that/does/not/exist seemed invalid?', - OutputInterface::VERBOSITY_VERY_VERBOSE, + true, + IOInterface::VERY_VERBOSE, ); /** diff --git a/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php b/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php index a8eb2a4c..03e4ee47 100644 --- a/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php +++ b/test/unit/DependencyResolver/ResolveDependencyWithComposerTest.php @@ -5,6 +5,7 @@ namespace Php\PieUnitTest\DependencyResolver; use Composer\Composer; +use Composer\IO\IOInterface; use Composer\IO\NullIO; use Composer\Package\CompletePackage; use Composer\Package\Link; @@ -30,7 +31,6 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(ResolveDependencyWithComposer::class)] final class ResolveDependencyWithComposerTest extends TestCase @@ -81,7 +81,7 @@ public function testPackageThatCanBeResolved(): void ); $package = (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))($this->composer, $targetPlatform, new RequestedPackageAndVersion('asgrim/example-pie-extension', '^1.0'), false); @@ -125,7 +125,7 @@ public function testPackageThatCannotBeResolvedThrowsException(array $platformOv $this->expectException(UnableToResolveRequirement::class); (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))( $this->composer, @@ -164,7 +164,7 @@ public function testUnresolvedPackageCanBeInstalledWithForceOption(array $platfo $this->expectException(UnableToResolveRequirement::class); $package = (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))( $this->composer, @@ -216,7 +216,7 @@ public function testZtsOnlyPackageCannotBeInstalledOnNtsSystem(): void $this->expectException(IncompatibleThreadSafetyMode::class); $this->expectExceptionMessage('This extension does not support being installed on a non-Thread Safe PHP installation'); (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))( $this->composer, @@ -265,7 +265,7 @@ public function testNtsOnlyPackageCannotBeInstalledOnZtsSystem(): void $this->expectException(IncompatibleThreadSafetyMode::class); $this->expectExceptionMessage('This extension does not support being installed on a Thread Safe PHP installation'); (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))( $this->composer, @@ -314,7 +314,7 @@ public function testExtensionCanOnlyBeInstalledIfOsFamilyIsCompatible(): void $this->expectException(IncompatibleOperatingSystemFamily::class); $this->expectExceptionMessage('This extension does not support the "linux" operating system family. It is compatible with the following families: "solaris", "darwin"'); (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))( $this->composer, @@ -363,7 +363,7 @@ public function testExtensionCanOnlyBeInstalledIfOsFamilyIsNotInCompatible(): vo $this->expectException(IncompatibleOperatingSystemFamily::class); $this->expectExceptionMessage('This extension does not support the "darwin" operating system family. It is incompatible with the following families: "darwin", "solaris".'); (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))( $this->composer, @@ -400,7 +400,7 @@ public function testPackageThatCanBeResolvedWithReplaceConflict(): void ); $package = (new ResolveDependencyWithComposer( - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), $this->createMock(QuieterConsoleIO::class), ))($this->composer, $targetPlatform, new RequestedPackageAndVersion('asgrim/example-pie-extension', '^1.0'), false); diff --git a/test/unit/Installing/Ini/AddExtensionToTheIniFileTest.php b/test/unit/Installing/Ini/AddExtensionToTheIniFileTest.php index 4fd0bdc8..d2a71a7b 100644 --- a/test/unit/Installing/Ini/AddExtensionToTheIniFileTest.php +++ b/test/unit/Installing/Ini/AddExtensionToTheIniFileTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; use Composer\Package\CompletePackage; use Php\Pie\DependencyResolver\Package; use Php\Pie\ExtensionName; @@ -17,7 +18,7 @@ use PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; use function chmod; use function file_get_contents; @@ -33,14 +34,14 @@ #[CoversClass(AddExtensionToTheIniFile::class)] final class AddExtensionToTheIniFileTest extends TestCase { - private BufferedOutput $output; + private BufferIO $io; private PhpBinaryPath&MockObject $mockPhpBinary; public function setUp(): void { parent::setUp(); - $this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $this->io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $this->mockPhpBinary = $this->createMock(PhpBinaryPath::class); } @@ -66,13 +67,13 @@ public function testReturnsFalseWhenFileIsNotWritableAndSudoDoesNotExist(): void null, ), $this->mockPhpBinary, - $this->output, + $this->io, null, )); self::assertStringContainsString( sprintf('PHP is configured to use %s, but it is not writable by PIE.', $unwritableFilename), - $this->output->fetch(), + $this->io->getOutput(), ); } finally { chmod($unwritableFilename, 644); @@ -102,7 +103,7 @@ public function testReturnsTrueWhenFileIsNotWritableAndSudoExists(): void null, ), $this->mockPhpBinary, - $this->output, + $this->io, null, )); } finally { @@ -134,13 +135,13 @@ public function testReturnsFalseWhenExistingIniCouldNotBeRead(): void null, ), $this->mockPhpBinary, - $this->output, + $this->io, null, )); self::assertStringContainsString( sprintf('Could not read %s to make a backup of it, aborting enablement of extension', $unreadableIniFile), - $this->output->fetch(), + $this->io->getOutput(), ); } finally { chmod($unreadableIniFile, 644); @@ -177,13 +178,13 @@ public function testReturnsFalseWhenExtensionWasAddedButPhpRuntimeDidNotLoadExte null, ), $this->mockPhpBinary, - $this->output, + $this->io, null, )); self::assertStringContainsString( 'Something went wrong enabling the foobar extension: Expected extension foobar to be loaded in PHP /path/to/php, but it was not detected.', - $this->output->fetch(), + $this->io->getOutput(), ); // Ensure the original INI file content was restored @@ -214,7 +215,7 @@ public function testReturnsTrueWhenExtensionAdded(): void null, ), $this->mockPhpBinary, - $this->output, + $this->io, null, )); @@ -228,7 +229,7 @@ public function testReturnsTrueWhenExtensionAdded(): void self::assertStringContainsString( sprintf('Enabled extension foobar in the INI file %s', $iniFile), - $this->output->fetch(), + $this->io->getOutput(), ); } finally { unlink($iniFile); @@ -257,7 +258,7 @@ public function testReturnsTrueWhenExtensionAddedWithAdditionalStep(): void null, ), $this->mockPhpBinary, - $this->output, + $this->io, static function () use (&$additionalStepInvoked): bool { $additionalStepInvoked = true; @@ -277,7 +278,7 @@ static function () use (&$additionalStepInvoked): bool { self::assertStringContainsString( sprintf('Enabled extension foobar in the INI file %s', $iniFile), - $this->output->fetch(), + $this->io->getOutput(), ); } finally { unlink($iniFile); diff --git a/test/unit/Installing/Ini/CheckAndAddExtensionToIniIfNeededTest.php b/test/unit/Installing/Ini/CheckAndAddExtensionToIniIfNeededTest.php index 69435e12..624fccdf 100644 --- a/test/unit/Installing/Ini/CheckAndAddExtensionToIniIfNeededTest.php +++ b/test/unit/Installing/Ini/CheckAndAddExtensionToIniIfNeededTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; use Composer\Package\CompletePackageInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -22,14 +23,14 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(CheckAndAddExtensionToIniIfNeeded::class)] final class CheckAndAddExtensionToIniIfNeededTest extends TestCase { private const INI_FILE = __DIR__ . '/../../../assets/example_ini_files/with_commented_extension.ini'; - private BufferedOutput $output; + private BufferIO $io; private PhpBinaryPath&MockObject $mockPhpBinary; private IsExtensionAlreadyInTheIniFile&MockObject $isExtensionAlreadyInTheIniFile; private AddExtensionToTheIniFile&MockObject $addExtensionToTheIniFile; @@ -41,7 +42,7 @@ public function setUp(): void { parent::setUp(); - $this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $this->io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $this->mockPhpBinary = $this->createMock(PhpBinaryPath::class); (fn () => $this->phpBinaryPath = '/path/to/php') @@ -96,13 +97,13 @@ public function testReturnsFalseWhenIniFileDoesNotExist(): void '/path/to/non/existent/php.ini', $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, null, )); self::assertStringContainsString( 'PHP is configured to use /path/to/non/existent/php.ini, but it did not exist, or is not readable by PIE.', - $this->output->fetch(), + $this->io->getOutput(), ); } @@ -117,7 +118,7 @@ public function testExtensionIsAlreadyEnabledButExtensionDoesNotLoad(): void $this->mockPhpBinary ->expects(self::once()) ->method('assertExtensionIsLoadedInRuntime') - ->with($this->downloadedPackage->package->extensionName(), $this->output) + ->with($this->downloadedPackage->package->extensionName(), $this->io) ->willThrowException(ExtensionIsNotLoaded::fromExpectedExtension( $this->mockPhpBinary, $this->downloadedPackage->package->extensionName(), @@ -131,18 +132,18 @@ public function testExtensionIsAlreadyEnabledButExtensionDoesNotLoad(): void self::INI_FILE, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, null, )); - $output = $this->output->fetch(); + $stringOutput = $this->io->getOutput(); self::assertStringContainsString( 'Extension is already enabled in the INI file', - $output, + $stringOutput, ); self::assertStringContainsString( 'Something went wrong verifying the foobar extension is enabled: Expected extension foobar to be loaded in PHP /path/to/php, but it was not detected.', - $output, + $stringOutput, ); } @@ -157,7 +158,7 @@ public function testExtensionIsAlreadyEnabledAndExtensionLoaded(): void $this->mockPhpBinary ->expects(self::once()) ->method('assertExtensionIsLoadedInRuntime') - ->with($this->downloadedPackage->package->extensionName(), $this->output); + ->with($this->downloadedPackage->package->extensionName(), $this->io); $this->addExtensionToTheIniFile ->expects(self::never()) @@ -167,14 +168,14 @@ public function testExtensionIsAlreadyEnabledAndExtensionLoaded(): void self::INI_FILE, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, null, )); - $output = $this->output->fetch(); + $stringOutput = $this->io->getOutput(); self::assertStringContainsString( 'Extension is already enabled in the INI file', - $output, + $stringOutput, ); } @@ -189,7 +190,7 @@ public function testExtensionIsAlreadyEnabledWithAdditionalStepAndExtensionLoade $this->mockPhpBinary ->expects(self::once()) ->method('assertExtensionIsLoadedInRuntime') - ->with($this->downloadedPackage->package->extensionName(), $this->output); + ->with($this->downloadedPackage->package->extensionName(), $this->io); $this->addExtensionToTheIniFile ->expects(self::never()) @@ -200,7 +201,7 @@ public function testExtensionIsAlreadyEnabledWithAdditionalStepAndExtensionLoade self::INI_FILE, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, static function () use (&$additionalStepInvoked): bool { $additionalStepInvoked = true; @@ -210,10 +211,9 @@ static function () use (&$additionalStepInvoked): bool { self::assertTrue($additionalStepInvoked, 'Failed asserting that the additional step was invoked'); - $output = $this->output->fetch(); self::assertStringContainsString( 'Extension is already enabled in the INI file', - $output, + $this->io->getOutput(), ); } @@ -236,7 +236,7 @@ public function testExtensionIsNotYetAdded(): void self::INI_FILE, $this->downloadedPackage->package, $this->targetPlatform->phpBinaryPath, - $this->output, + $this->io, ) ->willReturn(true); @@ -244,7 +244,7 @@ public function testExtensionIsNotYetAdded(): void self::INI_FILE, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, null, )); } @@ -268,7 +268,7 @@ public function testExtensionIsNotYetAddedButFailsToBeAdded(): void self::INI_FILE, $this->downloadedPackage->package, $this->targetPlatform->phpBinaryPath, - $this->output, + $this->io, ) ->willReturn(false); @@ -276,7 +276,7 @@ public function testExtensionIsNotYetAddedButFailsToBeAdded(): void self::INI_FILE, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, null, )); } diff --git a/test/unit/Installing/Ini/DockerPhpExtEnableTest.php b/test/unit/Installing/Ini/DockerPhpExtEnableTest.php index d27ee241..af43fb27 100644 --- a/test/unit/Installing/Ini/DockerPhpExtEnableTest.php +++ b/test/unit/Installing/Ini/DockerPhpExtEnableTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; use Composer\Package\CompletePackageInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -22,7 +23,7 @@ use PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; #[RequiresOperatingSystemFamily('Linux')] #[CoversClass(DockerPhpExtEnable::class)] @@ -32,7 +33,7 @@ final class DockerPhpExtEnableTest extends TestCase private const GOOD_DOCKER_PHP_EXT_ENABLE = __DIR__ . '/../../../assets/docker-php-ext-enable/good'; private const BAD_DOCKER_PHP_EXT_ENABLE = __DIR__ . '/../../../assets/docker-php-ext-enable/bad'; - private BufferedOutput $output; + private BufferIO $io; private PhpBinaryPath&MockObject $mockPhpBinary; private TargetPlatform $targetPlatform; private DownloadedPackage $downloadedPackage; @@ -42,7 +43,7 @@ public function setUp(): void { parent::setUp(); - $this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $this->io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $this->mockPhpBinary = $this->createMock(PhpBinaryPath::class); (fn () => $this->phpBinaryPath = '/path/to/php') @@ -101,7 +102,7 @@ public function testSetupReturnsFalseWhenWhenDockerPhpExtEnableIsNotInPath(): vo $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); } @@ -111,7 +112,7 @@ public function testReturnsTrueWhenDockerPhpExtEnableSuccessfullyEnablesExtensio $this->mockPhpBinary ->expects(self::once()) ->method('assertExtensionIsLoadedInRuntime') - ->with($this->downloadedPackage->package->extensionName(), $this->output); + ->with($this->downloadedPackage->package->extensionName(), $this->io); self::assertTrue( (new DockerPhpExtEnable(self::GOOD_DOCKER_PHP_EXT_ENABLE)) @@ -119,7 +120,7 @@ public function testReturnsTrueWhenDockerPhpExtEnableSuccessfullyEnablesExtensio $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); } @@ -136,7 +137,7 @@ public function testReturnsFalseWhenDockerPhpExtEnableFailsToBeRun(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); } @@ -146,7 +147,7 @@ public function testReturnsFalseWhenDockerPhpExtEnableFailsToAssertExtensionWasE $this->mockPhpBinary ->expects(self::once()) ->method('assertExtensionIsLoadedInRuntime') - ->with($this->downloadedPackage->package->extensionName(), $this->output) + ->with($this->downloadedPackage->package->extensionName(), $this->io) ->willThrowException(ExtensionIsNotLoaded::fromExpectedExtension( $this->mockPhpBinary, $this->downloadedPackage->package->extensionName(), @@ -158,7 +159,7 @@ public function testReturnsFalseWhenDockerPhpExtEnableFailsToAssertExtensionWasE $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); } diff --git a/test/unit/Installing/Ini/OndrejPhpenmodTest.php b/test/unit/Installing/Ini/OndrejPhpenmodTest.php index fc14406f..c8b18fbb 100644 --- a/test/unit/Installing/Ini/OndrejPhpenmodTest.php +++ b/test/unit/Installing/Ini/OndrejPhpenmodTest.php @@ -4,6 +4,8 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; +use Composer\IO\IOInterface; use Composer\Package\CompletePackageInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -23,7 +25,6 @@ use PHPUnit\Framework\Constraint\IsType; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Console\Output\OutputInterface; use function mkdir; @@ -42,7 +43,7 @@ final class OndrejPhpenmodTest extends TestCase private const GOOD_PHPENMOD = __DIR__ . '/../../../assets/phpenmod/good'; private const BAD_PHPENMOD = __DIR__ . '/../../../assets/phpenmod/bad'; - private BufferedOutput $output; + private BufferIO $io; private PhpBinaryPath&MockObject $mockPhpBinary; private CheckAndAddExtensionToIniIfNeeded&MockObject $checkAndAddExtensionToIniIfNeeded; private TargetPlatform $targetPlatform; @@ -53,7 +54,7 @@ public function setUp(): void { parent::setUp(); - $this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $this->io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $this->mockPhpBinary = $this->createMock(PhpBinaryPath::class); (fn () => $this->phpBinaryPath = '/path/to/php') @@ -138,7 +139,7 @@ public function testSetupReturnsFalseOnWindows(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); } @@ -159,7 +160,7 @@ public function testSetupReturnsFalseWhenPhpenmodNotInPath(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); } @@ -185,13 +186,13 @@ public function testSetupReturnsFalseWhenAdditionalPhpIniPathNotSet(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); self::assertStringContainsString( 'Additional INI file path was not set - may not be Ondrej PHP repo', - $this->output->fetch(), + $this->io->getOutput(), ); } @@ -215,13 +216,13 @@ public function testSetupReturnsFalseWhenModsAvailablePathDoesNotExist(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); self::assertStringContainsString( 'Mods available path ' . self::NON_EXISTENT_MODS_AVAILABLE_PATH . ' does not exist', - $this->output->fetch(), + $this->io->getOutput(), ); } @@ -245,13 +246,13 @@ public function testSetupReturnsFalseWhenModsAvailablePathNotADirectory(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); self::assertStringContainsString( 'Mods available path ' . __FILE__ . ' is not a directory', - $this->output->fetch(), + $this->io->getOutput(), ); } @@ -279,7 +280,7 @@ public function testSetupReturnsFalseWhenModsAvailablePathNotWritable(): void $expectedIniFile, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, self::isType(IsType::TYPE_CALLABLE), ) ->willReturnCallback( @@ -288,7 +289,7 @@ static function ( string $iniFile, TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, - OutputInterface $output, + IOInterface $io, callable $additionalEnableStep, ): bool { return $additionalEnableStep(); @@ -304,7 +305,7 @@ static function ( $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); @@ -331,7 +332,7 @@ public function testSetupReturnsFalseAndRemovesPieCreatedIniFileWhenPhpenmodAddi $expectedIniFile, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, self::isType(IsType::TYPE_CALLABLE), ) ->willReturnCallback( @@ -340,7 +341,7 @@ static function ( string $iniFile, TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, - OutputInterface $output, + IOInterface $io, callable $additionalEnableStep, ): bool { return $additionalEnableStep(); @@ -356,7 +357,7 @@ static function ( $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); @@ -364,7 +365,7 @@ static function ( self::assertStringContainsString( 'something bad happened', - $this->output->fetch(), + $this->io->getOutput(), ); rmdir($modsAvailablePath); @@ -390,7 +391,7 @@ public function testSetupReturnsFalseAndRemovesPieCreatedIniFileWhenCheckAndAddE $expectedIniFile, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, self::isType(IsType::TYPE_CALLABLE), ) ->willReturn(false); @@ -404,7 +405,7 @@ public function testSetupReturnsFalseAndRemovesPieCreatedIniFileWhenCheckAndAddE $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); @@ -433,7 +434,7 @@ public function testSetupReturnsTrueWhenExtensionIsEnabled(): void $expectedIniFile, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, self::isType(IsType::TYPE_CALLABLE), ) ->willReturnCallback( @@ -442,7 +443,7 @@ static function ( string $iniFile, TargetPlatform $targetPlatform, DownloadedPackage $downloadedPackage, - OutputInterface $output, + IOInterface $io, callable $additionalEnableStep, ): bool { return $additionalEnableStep(); @@ -458,7 +459,7 @@ static function ( $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, ), ); diff --git a/test/unit/Installing/Ini/PickBestSetupIniApproachTest.php b/test/unit/Installing/Ini/PickBestSetupIniApproachTest.php index 0786ee7f..ff3dd5c2 100644 --- a/test/unit/Installing/Ini/PickBestSetupIniApproachTest.php +++ b/test/unit/Installing/Ini/PickBestSetupIniApproachTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; use Composer\Package\CompletePackage; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -20,7 +21,7 @@ use Php\Pie\Platform\ThreadSafetyMode; use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(PickBestSetupIniApproach::class)] final class PickBestSetupIniApproachTest extends TestCase @@ -85,7 +86,7 @@ public function testCanBeUsedWithAllApproaches(): void public function testVerboseMessageIsEmittedSettingUpWithoutAnyApproaches(): void { - $output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); self::assertFalse((new PickBestSetupIniApproach([]))->setup( $this->targetPlatform(), @@ -101,19 +102,19 @@ public function testVerboseMessageIsEmittedSettingUpWithoutAnyApproaches(): void '/path/to/extracted/source', ), new BinaryFile('/path/to/extracted/source/module/foo.so', 'some-checksum'), - $output, + $io, )); - $outputString = $output->fetch(); + $stringOutput = $io->getOutput(); self::assertStringContainsString( 'No INI setup approaches can be used on this platform.', - $outputString, + $stringOutput, ); } public function testWorkingApproachIsUsed(): void { - $output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $one = $this->createMock(SetupIniApproach::class); $one->method('canBeUsed')->willReturn(true); @@ -136,19 +137,19 @@ public function testWorkingApproachIsUsed(): void '/path/to/extracted/source', ), new BinaryFile('/path/to/extracted/source/module/foo.so', 'some-checksum'), - $output, + $io, )); - $outputString = $output->fetch(); + $stringOutput = $io->getOutput(); self::assertStringContainsString( 'Trying to enable extension using MockObject_SetupIniApproach', - $outputString, + $stringOutput, ); } public function testSetupFailsWhenNoApproachesWork(): void { - $output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $one = $this->createMock(SetupIniApproach::class); $one->method('canBeUsed')->willReturn(true); @@ -171,13 +172,13 @@ public function testSetupFailsWhenNoApproachesWork(): void '/path/to/extracted/source', ), new BinaryFile('/path/to/extracted/source/module/foo.so', 'some-checksum'), - $output, + $io, )); - $outputString = $output->fetch(); + $stringOutput = $io->getOutput(); self::assertStringContainsString( 'None of the INI setup approaches succeeded.', - $outputString, + $stringOutput, ); } } diff --git a/test/unit/Installing/Ini/PreCheckExtensionAlreadyLoadedTest.php b/test/unit/Installing/Ini/PreCheckExtensionAlreadyLoadedTest.php index b22eff67..e04ad661 100644 --- a/test/unit/Installing/Ini/PreCheckExtensionAlreadyLoadedTest.php +++ b/test/unit/Installing/Ini/PreCheckExtensionAlreadyLoadedTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; use Composer\Package\CompletePackageInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -21,12 +22,12 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(PreCheckExtensionAlreadyLoaded::class)] final class PreCheckExtensionAlreadyLoadedTest extends TestCase { - private BufferedOutput $output; + private BufferIO $io; private PhpBinaryPath&MockObject $mockPhpBinary; private TargetPlatform $targetPlatform; private DownloadedPackage $downloadedPackage; @@ -37,7 +38,7 @@ public function setUp(): void { parent::setUp(); - $this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $this->io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $this->mockPhpBinary = $this->createMock(PhpBinaryPath::class); (fn () => $this->phpBinaryPath = '/path/to/php') @@ -82,13 +83,13 @@ public function testSetupReturnsTrueWhenExtAlreadyRuntimeLoaded(): void $this->mockPhpBinary ->expects(self::once()) ->method('assertExtensionIsLoadedInRuntime') - ->with($this->downloadedPackage->package->extensionName(), $this->output); + ->with($this->downloadedPackage->package->extensionName(), $this->io); self::assertTrue($this->preCheckExtensionAlreadyLoaded->setup( $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); } @@ -97,7 +98,7 @@ public function testSetupReturnsFalseWhenExtIsNotRuntimeLoaded(): void $this->mockPhpBinary ->expects(self::once()) ->method('assertExtensionIsLoadedInRuntime') - ->with($this->downloadedPackage->package->extensionName(), $this->output) + ->with($this->downloadedPackage->package->extensionName(), $this->io) ->willThrowException(ExtensionIsNotLoaded::fromExpectedExtension( $this->mockPhpBinary, $this->downloadedPackage->package->extensionName(), @@ -107,7 +108,7 @@ public function testSetupReturnsFalseWhenExtIsNotRuntimeLoaded(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); } } diff --git a/test/unit/Installing/Ini/RemoveIniEntryWithFileGetContentsTest.php b/test/unit/Installing/Ini/RemoveIniEntryWithFileGetContentsTest.php index 76259787..e4f20a95 100644 --- a/test/unit/Installing/Ini/RemoveIniEntryWithFileGetContentsTest.php +++ b/test/unit/Installing/Ini/RemoveIniEntryWithFileGetContentsTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\IOInterface; use Composer\Package\CompletePackageInterface; use Composer\Util\Filesystem; use Php\Pie\DependencyResolver\Package; @@ -20,7 +21,6 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\RequiresOperatingSystemFamily; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\OutputInterface; use Webmozart\Assert\Assert; use function file_get_contents; @@ -109,7 +109,7 @@ public function testRelevantIniFilesHaveExtensionRemoved(ExtensionType $extensio $affectedFiles = (new RemoveIniEntryWithFileGetContents())( $package, $targetPlatform, - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), ); self::assertSame( @@ -163,7 +163,7 @@ public function testNonExistentAdditionalIniDirectoryDoesNotCrash(ExtensionType (new RemoveIniEntryWithFileGetContents())( $package, $targetPlatform, - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), ), ); } @@ -207,7 +207,7 @@ public function testSymlinkedIniFilesAreResolved(): void $affectedFiles = (new RemoveIniEntryWithFileGetContents())( $package, $targetPlatform, - $this->createMock(OutputInterface::class), + $this->createMock(IOInterface::class), ); self::assertSame( diff --git a/test/unit/Installing/Ini/StandardAdditionalPhpIniDirectoryTest.php b/test/unit/Installing/Ini/StandardAdditionalPhpIniDirectoryTest.php index 2af67d0a..9c7681eb 100644 --- a/test/unit/Installing/Ini/StandardAdditionalPhpIniDirectoryTest.php +++ b/test/unit/Installing/Ini/StandardAdditionalPhpIniDirectoryTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; use Composer\Package\CompletePackageInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -21,7 +22,7 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; use function mkdir; use function rmdir; @@ -35,7 +36,7 @@ #[CoversClass(StandardAdditionalPhpIniDirectory::class)] final class StandardAdditionalPhpIniDirectoryTest extends TestCase { - private BufferedOutput $output; + private BufferIO $io; private PhpBinaryPath&MockObject $mockPhpBinary; private CheckAndAddExtensionToIniIfNeeded&MockObject $checkAndAddExtensionToIniIfNeeded; private TargetPlatform $targetPlatform; @@ -47,7 +48,7 @@ public function setUp(): void { parent::setUp(); - $this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $this->io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $this->mockPhpBinary = $this->createMock(PhpBinaryPath::class); (fn () => $this->phpBinaryPath = '/path/to/php') @@ -119,7 +120,7 @@ public function testSetupReturnsWhenAdditionalPhpIniDirectoryIsNotSet(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); } @@ -138,11 +139,11 @@ public function testSetupReturnsWhenAdditionalPhpIniDirectoryDoesNotExist(): voi $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); self::assertStringContainsString( 'PHP is configured to use additional INI file path /path/to/something/does/not/exist, but it did not exist', - $this->output->fetch(), + $this->io->getOutput(), ); } @@ -166,7 +167,7 @@ public function testReturnsTrueWhenCheckAndAddExtensionIsInvoked(): void $expectedIniFile, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, ) ->willReturn(true); @@ -174,7 +175,7 @@ public function testReturnsTrueWhenCheckAndAddExtensionIsInvoked(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); self::assertFileExists($expectedIniFile); @@ -202,7 +203,7 @@ public function testReturnsFalseAndRemovesPieCreatedIniFileWhenCheckAndAddExtens $expectedIniFile, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, ) ->willReturn(false); @@ -210,7 +211,7 @@ public function testReturnsFalseAndRemovesPieCreatedIniFileWhenCheckAndAddExtens $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); self::assertFileDoesNotExist($expectedIniFile); @@ -238,7 +239,7 @@ public function testReturnsFalseAndLeavesNonPieCreatedIniFileWhenCheckAndAddExte $expectedIniFile, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, ) ->willReturn(false); @@ -246,7 +247,7 @@ public function testReturnsFalseAndLeavesNonPieCreatedIniFileWhenCheckAndAddExte $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); self::assertFileExists($expectedIniFile); diff --git a/test/unit/Installing/Ini/StandardSinglePhpIniTest.php b/test/unit/Installing/Ini/StandardSinglePhpIniTest.php index 7518880b..d93f0f22 100644 --- a/test/unit/Installing/Ini/StandardSinglePhpIniTest.php +++ b/test/unit/Installing/Ini/StandardSinglePhpIniTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\Ini; +use Composer\IO\BufferIO; use Composer\Package\CompletePackageInterface; use Php\Pie\DependencyResolver\Package; use Php\Pie\Downloading\DownloadedPackage; @@ -21,14 +22,14 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; #[CoversClass(StandardSinglePhpIni::class)] final class StandardSinglePhpIniTest extends TestCase { private const INI_FILE = __DIR__ . '/../../../assets/example_ini_files/with_commented_extension.ini'; - private BufferedOutput $output; + private BufferIO $io; private PhpBinaryPath&MockObject $mockPhpBinary; private CheckAndAddExtensionToIniIfNeeded&MockObject $checkAndAddExtensionToIniIfNeeded; private TargetPlatform $targetPlatform; @@ -40,7 +41,7 @@ public function setUp(): void { parent::setUp(); - $this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); + $this->io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); $this->mockPhpBinary = $this->createMock(PhpBinaryPath::class); (fn () => $this->phpBinaryPath = '/path/to/php') @@ -112,7 +113,7 @@ public function testSetupReturnsWhenIniFileIsNotSet(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); } @@ -130,7 +131,7 @@ public function testReturnsTrueWhenCheckAndAddExtensionIsInvoked(): void self::INI_FILE, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, ) ->willReturn(true); @@ -138,7 +139,7 @@ public function testReturnsTrueWhenCheckAndAddExtensionIsInvoked(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); } @@ -156,7 +157,7 @@ public function testReturnsFalseWhenCheckAndAddExtensionIsInvoked(): void self::INI_FILE, $this->targetPlatform, $this->downloadedPackage, - $this->output, + $this->io, ) ->willReturn(false); @@ -164,7 +165,7 @@ public function testReturnsFalseWhenCheckAndAddExtensionIsInvoked(): void $this->targetPlatform, $this->downloadedPackage, $this->binaryFile, - $this->output, + $this->io, )); } } diff --git a/test/unit/Installing/InstallForPhpProject/InstallPiePackageFromPathTest.php b/test/unit/Installing/InstallForPhpProject/InstallPiePackageFromPathTest.php index 971a035c..20df724f 100644 --- a/test/unit/Installing/InstallForPhpProject/InstallPiePackageFromPathTest.php +++ b/test/unit/Installing/InstallForPhpProject/InstallPiePackageFromPathTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\InstallForPhpProject; +use Composer\IO\BufferIO; use Composer\Package\RootPackage; use Php\Pie\Command\InvokeSubCommand; use Php\Pie\ComposerIntegration\PieJsonEditor; @@ -14,7 +15,6 @@ use RuntimeException; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Output\BufferedOutput; #[CoversClass(InstallPiePackageFromPath::class)] final class InstallPiePackageFromPathTest extends TestCase @@ -24,7 +24,7 @@ final class InstallPiePackageFromPathTest extends TestCase private InvokeSubCommand&MockObject $invokeSubCommand; private PieJsonEditor&MockObject $pieJsonEditor; private InputInterface&MockObject $input; - private BufferedOutput $output; + private BufferIO $io; public function setUp(): void { @@ -35,7 +35,7 @@ public function setUp(): void $this->invokeSubCommand = $this->createMock(InvokeSubCommand::class); $this->pieJsonEditor = $this->createMock(PieJsonEditor::class); $this->input = $this->createMock(InputInterface::class); - $this->output = new BufferedOutput(); + $this->io = new BufferIO(); } public function testInvokeWithSuccessfulSubCommand(): void @@ -62,7 +62,6 @@ public function testInvokeWithSuccessfulSubCommand(): void 'requested-package-and-version' => 'foo/bar:*@dev', ], $this->input, - $this->output, ) ->willReturn(Command::SUCCESS); @@ -74,7 +73,7 @@ public function testInvokeWithSuccessfulSubCommand(): void $this->rootPackage, $this->pieJsonEditor, $this->input, - $this->output, + $this->io, ), ); } @@ -105,7 +104,6 @@ public function testInvokeWithSubCommandException(): void 'requested-package-and-version' => 'foo/bar:*@dev', ], $this->input, - $this->output, ) ->willThrowException(new RuntimeException('oh no')); @@ -119,7 +117,7 @@ public function testInvokeWithSubCommandException(): void $this->rootPackage, $this->pieJsonEditor, $this->input, - $this->output, + $this->io, ); } } diff --git a/test/unit/Installing/InstallForPhpProject/InstallSelectedPackageTest.php b/test/unit/Installing/InstallForPhpProject/InstallSelectedPackageTest.php index f9c63e32..44623dab 100644 --- a/test/unit/Installing/InstallForPhpProject/InstallSelectedPackageTest.php +++ b/test/unit/Installing/InstallForPhpProject/InstallSelectedPackageTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Installing\InstallForPhpProject; +use Composer\IO\BufferIO; use Composer\Util\Platform; use Php\Pie\File\FullPathToSelf; use Php\Pie\Installing\InstallForPhpProject\InstallSelectedPackage; @@ -12,7 +13,6 @@ use Symfony\Component\Console\Input\ArrayInput; use Symfony\Component\Console\Input\InputDefinition; use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\BufferedOutput; use function getcwd; use function trim; @@ -27,20 +27,23 @@ public function testWithPieCli(): void { $_SERVER['PHP_SELF'] = Platform::isWindows() ? self::FAKE_HAPPY_BAT : self::FAKE_HAPPY_SH; - $input = new ArrayInput( + $input = new ArrayInput( ['--with-php-config' => '/path/to/php/config'], new InputDefinition([ new InputOption('with-php-config', null, InputOption::VALUE_REQUIRED), ]), ); - $output = new BufferedOutput(); + $io = new BufferIO(); (new InstallSelectedPackage(new FullPathToSelf(getcwd())))->withPieCli( 'foo/bar', $input, - $output, + $io, ); - self::assertSame('> Params passed: install foo/bar --with-php-config /path/to/php/config', trim($output->fetch())); + self::assertSame( + '> Params passed: install foo/bar --with-php-config /path/to/php/config', + trim($io->getOutput()), + ); } } diff --git a/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php b/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php index f7ff6f0d..8aeb4d05 100644 --- a/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php +++ b/test/unit/Platform/TargetPhp/PhpBinaryPathTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\Platform\TargetPhp; +use Composer\IO\BufferIO; use Composer\Util\Platform; use Php\Pie\ExtensionName; use Php\Pie\Platform\Architecture; @@ -16,7 +17,7 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; +use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Process\PhpExecutableFinder; use function array_column; @@ -313,12 +314,12 @@ public function testAssertExtensionIsLoaded(): void self::fail('Core extension is not loaded, this is quite unexpected...'); } - $output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); - $php->assertExtensionIsLoadedInRuntime(ExtensionName::normaliseFromString('Core'), $output); + $io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); + $php->assertExtensionIsLoadedInRuntime(ExtensionName::normaliseFromString('Core'), $io); self::assertStringContainsString( 'Successfully asserted that extension Core is loaded in runtime.', - $output->fetch(), + $io->getOutput(), ); } @@ -331,12 +332,12 @@ public function testAssertDifferentCasedExtensionIsLoaded(): void self::fail('Core extension is not loaded, this is quite unexpected...'); } - $output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE); - $php->assertExtensionIsLoadedInRuntime(ExtensionName::normaliseFromString('CORE'), $output); + $io = new BufferIO(verbosity: OutputInterface::VERBOSITY_VERBOSE); + $php->assertExtensionIsLoadedInRuntime(ExtensionName::normaliseFromString('CORE'), $io); self::assertStringContainsString( 'Successfully asserted that extension CORE is loaded in runtime.', - $output->fetch(), + $io->getOutput(), ); } diff --git a/test/unit/SelfManage/Verify/FallbackVerificationUsingOpenSslTest.php b/test/unit/SelfManage/Verify/FallbackVerificationUsingOpenSslTest.php index 7122661b..f19adc95 100644 --- a/test/unit/SelfManage/Verify/FallbackVerificationUsingOpenSslTest.php +++ b/test/unit/SelfManage/Verify/FallbackVerificationUsingOpenSslTest.php @@ -5,6 +5,7 @@ namespace Php\PieUnitTest\SelfManage\Verify; use Composer\Downloader\TransportException; +use Composer\IO\BufferIO; use Composer\Util\AuthHelper; use Composer\Util\Http\Response; use Composer\Util\HttpDownloader; @@ -15,7 +16,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; use ThePhpFoundation\Attestation\Verification\VerifyAttestationWithOpenSsl; use function assert; @@ -48,7 +48,7 @@ final class FallbackVerificationUsingOpenSslTest extends TestCase private BinaryFile $downloadedPhar; private HttpDownloader&MockObject $httpDownloader; private AuthHelper&MockObject $authHelper; - private BufferedOutput $output; + private BufferIO $io; private FallbackVerificationUsingOpenSsl $verifier; /** @var non-empty-string */ private string $trustedRootFilePath; @@ -62,7 +62,7 @@ public function setUp(): void $this->httpDownloader = $this->createMock(HttpDownloader::class); $this->authHelper = $this->createMock(AuthHelper::class); - $this->output = new BufferedOutput(); + $this->io = new BufferIO(); $trustedRootFilePath = tempnam(sys_get_temp_dir(), 'pie_test_trusted_root_file_path'); assert(is_string($trustedRootFilePath)); @@ -198,9 +198,9 @@ public function testSuccessfulVerify(): void $this->mockAttestationResponse($this->downloadedPhar->checksum, $dsseEnvelopePayload, $signature, $pemCertificate); - $this->verifier->verify($this->release, $this->downloadedPhar, $this->output); + $this->verifier->verify($this->release, $this->downloadedPhar, $this->io); - self::assertStringContainsString('Verified the new PIE version (using fallback verification)', $this->output->fetch()); + self::assertStringContainsString('Verified the new PIE version (using fallback verification)', $this->io->getOutput()); } public function testFailedToVerifyBecauseDigestMismatch(): void @@ -223,7 +223,7 @@ public function testFailedToVerifyBecauseDigestMismatch(): void $this->mockAttestationResponse($this->downloadedPhar->checksum, $dsseEnvelopePayload, $signature, $pemCertificate); $this->expectException(FailedToVerifyRelease::class); - $this->verifier->verify($this->release, $this->downloadedPhar, $this->output); + $this->verifier->verify($this->release, $this->downloadedPhar, $this->io); } public function testFailedToVerifyBecauseSignatureVerificationFailed(): void @@ -259,7 +259,7 @@ public function testFailedToVerifyBecauseSignatureVerificationFailed(): void ); $this->expectException(FailedToVerifyRelease::class); - $this->verifier->verify($this->release, $this->downloadedPhar, $this->output); + $this->verifier->verify($this->release, $this->downloadedPhar, $this->io); } public function testFailedToVerifyBecauseDigestNotFoundOnGitHub(): void @@ -279,6 +279,6 @@ public function testFailedToVerifyBecauseDigestNotFoundOnGitHub(): void ->willThrowException($transportException); $this->expectException(FailedToVerifyRelease::class); - $this->verifier->verify($this->release, $this->downloadedPhar, $this->output); + $this->verifier->verify($this->release, $this->downloadedPhar, $this->io); } } diff --git a/test/unit/SelfManage/Verify/GithubCliAttestationVerificationTest.php b/test/unit/SelfManage/Verify/GithubCliAttestationVerificationTest.php index ad9863d4..1bdfd163 100644 --- a/test/unit/SelfManage/Verify/GithubCliAttestationVerificationTest.php +++ b/test/unit/SelfManage/Verify/GithubCliAttestationVerificationTest.php @@ -4,6 +4,7 @@ namespace Php\PieUnitTest\SelfManage\Verify; +use Composer\IO\BufferIO; use Composer\Util\Platform; use Php\Pie\File\BinaryFile; use Php\Pie\SelfManage\Update\ReleaseMetadata; @@ -13,7 +14,6 @@ use PHPUnit\Framework\Attributes\CoversClass; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; -use Symfony\Component\Console\Output\BufferedOutput; use Symfony\Component\Process\ExecutableFinder; #[CoversClass(GithubCliAttestationVerification::class)] @@ -25,7 +25,7 @@ final class GithubCliAttestationVerificationTest extends TestCase private const FAKE_GH_CLI_UNHAPPY_BAT = __DIR__ . '/../../../assets/fake-gh-cli/unhappy.bat'; private ExecutableFinder&MockObject $executableFinder; - private BufferedOutput $output; + private BufferIO $io; private GithubCliAttestationVerification $verifier; public function setUp(): void @@ -33,7 +33,7 @@ public function setUp(): void parent::setUp(); $this->executableFinder = $this->createMock(ExecutableFinder::class); - $this->output = new BufferedOutput(); + $this->io = new BufferIO(); $this->verifier = new GithubCliAttestationVerification($this->executableFinder); } @@ -44,9 +44,9 @@ public function testPassingVerification(): void ->method('find') ->willReturn(Platform::isWindows() ? self::FAKE_GH_CLI_HAPPY_BAT : self::FAKE_GH_CLI_HAPPY_SH); - $this->verifier->verify(new ReleaseMetadata('1.2.3', 'https://path/to/download'), new BinaryFile('/path/to/phar', 'some-checksum'), $this->output); + $this->verifier->verify(new ReleaseMetadata('1.2.3', 'https://path/to/download'), new BinaryFile('/path/to/phar', 'some-checksum'), $this->io); - self::assertStringContainsString('Verified the new PIE version', $this->output->fetch()); + self::assertStringContainsString('Verified the new PIE version', $this->io->getOutput()); } public function testCannotFindGhCli(): void @@ -56,7 +56,7 @@ public function testCannotFindGhCli(): void ->willReturn(null); $this->expectException(GithubCliNotAvailable::class); - $this->verifier->verify(new ReleaseMetadata('1.2.3', 'https://path/to/download'), new BinaryFile('/path/to/phar', 'some-checksum'), $this->output); + $this->verifier->verify(new ReleaseMetadata('1.2.3', 'https://path/to/download'), new BinaryFile('/path/to/phar', 'some-checksum'), $this->io); } public function testFailingVerification(): void @@ -66,6 +66,6 @@ public function testFailingVerification(): void ->willReturn(Platform::isWindows() ? self::FAKE_GH_CLI_UNHAPPY_BAT : self::FAKE_GH_CLI_UNHAPPY_SH); $this->expectException(FailedToVerifyRelease::class); - $this->verifier->verify(new ReleaseMetadata('1.2.3', 'https://path/to/download'), new BinaryFile('/path/to/phar', 'some-checksum'), $this->output); + $this->verifier->verify(new ReleaseMetadata('1.2.3', 'https://path/to/download'), new BinaryFile('/path/to/phar', 'some-checksum'), $this->io); } } From f24fa3a23162b8a05bd26fbc077e039ec79eeb80 Mon Sep 17 00:00:00 2001 From: James Titcumb Date: Thu, 16 Oct 2025 19:44:22 +0100 Subject: [PATCH 2/2] Update errors to use writeError --- src/Command/BuildCommand.php | 6 +++--- src/Command/CommandHelper.php | 10 +++++----- src/Command/DownloadCommand.php | 6 +++--- src/Command/InfoCommand.php | 4 ++-- src/Command/InstallCommand.php | 6 +++--- src/Command/InstallExtensionsForProjectCommand.php | 6 +++--- src/Command/SelfUpdateCommand.php | 8 ++++---- src/Command/SelfVerifyCommand.php | 4 ++-- src/Command/UninstallCommand.php | 2 +- .../Listeners/RemoveUnrelatedInstallOperations.php | 4 ++-- src/ComposerIntegration/PiePackageInstaller.php | 4 ++-- src/Installing/Ini/AddExtensionToTheIniFile.php | 2 +- .../Ini/CheckAndAddExtensionToIniIfNeeded.php | 2 +- src/Installing/Ini/DockerPhpExtEnable.php | 4 ++-- src/Installing/Ini/OndrejPhpenmod.php | 2 +- .../Ini/RemoveIniEntryWithFileGetContents.php | 2 +- .../Verify/VerifyPieReleaseUsingAttestation.php | 2 +- 17 files changed, 37 insertions(+), 37 deletions(-) diff --git a/src/Command/BuildCommand.php b/src/Command/BuildCommand.php index 38027290..f161c0d5 100644 --- a/src/Command/BuildCommand.php +++ b/src/Command/BuildCommand.php @@ -92,8 +92,8 @@ public function execute(InputInterface $input, OutputInterface $output): int $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $this->io->write(''); - $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->writeError(''); + $this->io->writeError('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } @@ -128,7 +128,7 @@ public function execute(InputInterface $input, OutputInterface $output): int false, ); } catch (ComposerRunFailed $composerRunFailed) { - $this->io->write('' . $composerRunFailed->getMessage() . ''); + $this->io->writeError('' . $composerRunFailed->getMessage() . ''); return $composerRunFailed->getCode(); } diff --git a/src/Command/CommandHelper.php b/src/Command/CommandHelper.php index 2a17be7f..44e6ef42 100644 --- a/src/Command/CommandHelper.php +++ b/src/Command/CommandHelper.php @@ -365,9 +365,9 @@ public static function handlePackageNotFound( $requestedPackageName = substr($requestedPackageName, 4); } - $io->write(''); - $io->write(sprintf('Could not install package: %s', $requestedPackageName)); - $io->write($exception->getMessage()); + $io->writeError(''); + $io->writeError(sprintf('Could not install package: %s', $requestedPackageName)); + $io->writeError($exception->getMessage()); try { $matches = array_map( @@ -382,7 +382,7 @@ static function (array $match) use ($io, $pieComposer): array { ->extensionName() ->name(); } catch (Throwable $t) { - $io->write( + $io->writeError( sprintf( 'Tried looking up extension name for %s, but failed: %s', $match['name'], @@ -421,7 +421,7 @@ static function (array $match) use ($io): void { ); } } catch (OutOfRangeException) { - $io->write( + $io->writeError( sprintf( 'Tried searching for "%s", but nothing was found.', $requestedPackageName, diff --git a/src/Command/DownloadCommand.php b/src/Command/DownloadCommand.php index b921fb98..b0dcac33 100644 --- a/src/Command/DownloadCommand.php +++ b/src/Command/DownloadCommand.php @@ -94,8 +94,8 @@ public function execute(InputInterface $input, OutputInterface $output): int $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $this->io->write(''); - $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->writeError(''); + $this->io->writeError('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } @@ -112,7 +112,7 @@ public function execute(InputInterface $input, OutputInterface $output): int false, ); } catch (ComposerRunFailed $composerRunFailed) { - $this->io->write('' . $composerRunFailed->getMessage() . ''); + $this->io->writeError('' . $composerRunFailed->getMessage() . ''); return $composerRunFailed->getCode(); } diff --git a/src/Command/InfoCommand.php b/src/Command/InfoCommand.php index 554d43a3..4c0d5d4c 100644 --- a/src/Command/InfoCommand.php +++ b/src/Command/InfoCommand.php @@ -98,8 +98,8 @@ public function execute(InputInterface $input, OutputInterface $output): int $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $this->io->write(''); - $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->writeError(''); + $this->io->writeError('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } diff --git a/src/Command/InstallCommand.php b/src/Command/InstallCommand.php index d3d98f3e..d4793a7c 100644 --- a/src/Command/InstallCommand.php +++ b/src/Command/InstallCommand.php @@ -106,8 +106,8 @@ public function execute(InputInterface $input, OutputInterface $output): int $this->container, ); } catch (BundledPhpExtensionRefusal $bundledPhpExtensionRefusal) { - $this->io->write(''); - $this->io->write('' . $bundledPhpExtensionRefusal->getMessage() . ''); + $this->io->writeError(''); + $this->io->writeError('' . $bundledPhpExtensionRefusal->getMessage() . ''); return self::INVALID; } @@ -142,7 +142,7 @@ public function execute(InputInterface $input, OutputInterface $output): int true, ); } catch (ComposerRunFailed $composerRunFailed) { - $this->io->write('' . $composerRunFailed->getMessage() . ''); + $this->io->writeError('' . $composerRunFailed->getMessage() . ''); return $composerRunFailed->getCode(); } diff --git a/src/Command/InstallExtensionsForProjectCommand.php b/src/Command/InstallExtensionsForProjectCommand.php index 8da33125..d0308f4e 100644 --- a/src/Command/InstallExtensionsForProjectCommand.php +++ b/src/Command/InstallExtensionsForProjectCommand.php @@ -101,7 +101,7 @@ public function execute(InputInterface $input, OutputInterface $output): int if (ExtensionType::isValid($rootPackage->getType())) { $cwd = realpath(getcwd()); if (! is_string($cwd) || $cwd === '') { - $this->io->write('Failed to determine current working directory.'); + $this->io->writeError('Failed to determine current working directory.'); $restoreWorkingDir(); @@ -124,7 +124,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $allowNonInteractive = $input->hasOption(CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL) && $input->getOption(CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL); if (! Platform::isInteractive() && ! $allowNonInteractive) { - $this->io->write(sprintf( + $this->io->writeError(sprintf( 'Aborting! You are not running in interactive mode, and --%s was not specified.', CommandHelper::OPTION_ALLOW_NON_INTERACTIVE_PROJECT_INSTALL, )); @@ -226,7 +226,7 @@ function (Link $link) use ($pieComposer, $phpEnabledExtensions, $installedPiePac $anyErrorsHappened = true; // @todo Figure out if there is a way to improve this, safely - $this->io->write(sprintf( + $this->io->writeError(sprintf( "Multiple packages were found for %s:\n %s\n\nThis means you cannot `pie install` this project interactively for now.", $extension->nameWithExtPrefix(), implode("\n ", array_column($matches, 'name')), diff --git a/src/Command/SelfUpdateCommand.php b/src/Command/SelfUpdateCommand.php index afabae09..7d326bf6 100644 --- a/src/Command/SelfUpdateCommand.php +++ b/src/Command/SelfUpdateCommand.php @@ -84,7 +84,7 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { if (! PieVersion::isPharBuild()) { - $this->io->write('Aborting! You are not running a PHAR, cannot self-update.'); + $this->io->writeError('Aborting! You are not running a PHAR, cannot self-update.'); return Command::FAILURE; } @@ -131,7 +131,7 @@ public function execute(InputInterface $input, OutputInterface $output): int try { $latestRelease = $fetchLatestPieRelease->latestReleaseMetadata($updateChannel); } catch (Throwable $throwable) { - $this->io->write(sprintf('%s', $throwable->getMessage())); + $this->io->writeError(sprintf('%s', $throwable->getMessage())); return Command::FAILURE; } @@ -165,13 +165,13 @@ public function execute(InputInterface $input, OutputInterface $output): int try { $verifyPiePhar->verify($latestRelease, $pharFilename, $this->io); } catch (FailedToVerifyRelease $failedToVerifyRelease) { - $this->io->write(sprintf( + $this->io->writeError(sprintf( '❌ Failed to verify the pie.phar release %s: %s', $latestRelease->tag, $failedToVerifyRelease->getMessage(), )); - $this->io->write('This means I could not verify that the PHAR we tried to update to was authentic, so I am aborting the self-update.'); + $this->io->writeError('This means I could not verify that the PHAR we tried to update to was authentic, so I am aborting the self-update.'); unlink($pharFilename->filePath); return Command::FAILURE; diff --git a/src/Command/SelfVerifyCommand.php b/src/Command/SelfVerifyCommand.php index f388287d..1d1c7500 100644 --- a/src/Command/SelfVerifyCommand.php +++ b/src/Command/SelfVerifyCommand.php @@ -42,7 +42,7 @@ public function configure(): void public function execute(InputInterface $input, OutputInterface $output): int { if (! PieVersion::isPharBuild()) { - $this->io->write('Aborting! You are not running a PHAR, cannot self-verify.'); + $this->io->writeError('Aborting! You are not running a PHAR, cannot self-verify.'); return Command::FAILURE; } @@ -54,7 +54,7 @@ public function execute(InputInterface $input, OutputInterface $output): int try { $verifyPiePhar->verify($latestRelease, $pharFilename, $this->io); } catch (FailedToVerifyRelease $failedToVerifyRelease) { - $this->io->write(sprintf( + $this->io->writeError(sprintf( '❌ Failed to verify the pie.phar release %s: %s', $latestRelease->tag, $failedToVerifyRelease->getMessage(), diff --git a/src/Command/UninstallCommand.php b/src/Command/UninstallCommand.php index e2aab0b8..612dfdf3 100644 --- a/src/Command/UninstallCommand.php +++ b/src/Command/UninstallCommand.php @@ -76,7 +76,7 @@ public function execute(InputInterface $input, OutputInterface $output): int $piePackage = $this->findPiePackageByPackageName($packageToRemove, $composer); if ($piePackage === null) { - $this->io->write('No package found: ' . $packageToRemove . ''); + $this->io->writeError('No package found: ' . $packageToRemove . ''); return 1; } diff --git a/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php b/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php index 0e290d66..c26e7384 100644 --- a/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php +++ b/src/ComposerIntegration/Listeners/RemoveUnrelatedInstallOperations.php @@ -47,7 +47,7 @@ public function __invoke(InstallerEvent $installerEvent): void $installerEvent->getTransaction()?->getOperations() ?? [], function (OperationInterface $operation) use ($pieOutput): bool { if (! $operation instanceof InstallOperation && ! $operation instanceof UninstallOperation) { - $pieOutput->write( + $pieOutput->writeError( sprintf( 'Unexpected operation during installer: %s', $operation::class, @@ -61,7 +61,7 @@ function (OperationInterface $operation) use ($pieOutput): bool { $isRequestedPiePackage = $this->composerRequest->requestedPackage->package === $operation->getPackage()->getName(); if (! $isRequestedPiePackage) { - $pieOutput->write( + $pieOutput->writeError( sprintf( 'Filtering package %s from install operations, as it was not the requested package', $operation->getPackage()->getName(), diff --git a/src/ComposerIntegration/PiePackageInstaller.php b/src/ComposerIntegration/PiePackageInstaller.php index 0a218708..65268462 100644 --- a/src/ComposerIntegration/PiePackageInstaller.php +++ b/src/ComposerIntegration/PiePackageInstaller.php @@ -53,7 +53,7 @@ public function install(InstalledRepositoryInterface $repo, PackageInterface $pa } if (! $composerPackage instanceof CompletePackage) { - $io->write(sprintf( + $io->writeError(sprintf( 'Not using PIE to install %s as it was not a Complete Package', $composerPackage->getName(), )); @@ -95,7 +95,7 @@ public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $ } if (! $composerPackage instanceof CompletePackage) { - $io->write(sprintf( + $io->writeError(sprintf( 'Not using PIE to install %s as it was not a Complete Package', $composerPackage->getName(), )); diff --git a/src/Installing/Ini/AddExtensionToTheIniFile.php b/src/Installing/Ini/AddExtensionToTheIniFile.php index f62ccb04..14b7ba12 100644 --- a/src/Installing/Ini/AddExtensionToTheIniFile.php +++ b/src/Installing/Ini/AddExtensionToTheIniFile.php @@ -93,7 +93,7 @@ public function __invoke( } catch (Throwable $anything) { SudoFilePut::contents($ini, $originalIniContent); - $io->write(sprintf( + $io->writeError(sprintf( 'Something went wrong enabling the %s extension: %s', $package->extensionName()->name(), $anything->getMessage(), diff --git a/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php b/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php index 9c587389..69c0aee4 100644 --- a/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php +++ b/src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php @@ -63,7 +63,7 @@ public function __invoke( return true; } catch (Throwable $anything) { - $io->write(sprintf( + $io->writeError(sprintf( 'Something went wrong verifying the %s extension is enabled: %s', $downloadedPackage->package->extensionName()->name(), $anything->getMessage(), diff --git a/src/Installing/Ini/DockerPhpExtEnable.php b/src/Installing/Ini/DockerPhpExtEnable.php index 56135279..5dab9c13 100644 --- a/src/Installing/Ini/DockerPhpExtEnable.php +++ b/src/Installing/Ini/DockerPhpExtEnable.php @@ -43,7 +43,7 @@ public function setup( try { $enableOutput = Process::run([$dockerPhpExtEnable, $downloadedPackage->package->extensionName()->name()]); } catch (ProcessFailedException $processFailed) { - $io->write( + $io->writeError( sprintf( 'Could not enable extension %s using %s. Exception was: %s', $downloadedPackage->package->extensionName()->name(), @@ -64,7 +64,7 @@ public function setup( return true; } catch (ExtensionIsNotLoaded) { - $io->write( + $io->writeError( sprintf( 'Asserting that extension %s was enabled using %s failed. Output was: %s', $downloadedPackage->package->extensionName()->name(), diff --git a/src/Installing/Ini/OndrejPhpenmod.php b/src/Installing/Ini/OndrejPhpenmod.php index a167b562..d0320901 100644 --- a/src/Installing/Ini/OndrejPhpenmod.php +++ b/src/Installing/Ini/OndrejPhpenmod.php @@ -170,7 +170,7 @@ static function () use ($needSudo, $phpenmodPath, $targetPlatform, $downloadedPa return true; } catch (ProcessFailedException $processFailedException) { - $io->write( + $io->writeError( sprintf( 'Failed to use %s to enable %s for PHP %s: %s', $phpenmodPath, diff --git a/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php b/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php index fd826223..8b39a8d5 100644 --- a/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php +++ b/src/Installing/Ini/RemoveIniEntryWithFileGetContents.php @@ -96,7 +96,7 @@ static function (string $iniFile) use (&$updatedIniFiles, $regex, $package, $io) try { SudoFilePut::contents($iniFile, $replacedContent); } catch (FailedToWriteFile) { - $io->write(sprintf( + $io->writeError(sprintf( 'Failed to remove extension "%s" from INI file "%s"', $package->extensionName()->name(), $iniFile, diff --git a/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php b/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php index 6ea97a88..db4135de 100644 --- a/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php +++ b/src/SelfManage/Verify/VerifyPieReleaseUsingAttestation.php @@ -34,7 +34,7 @@ public function verify(ReleaseMetadata $releaseMetadata, BinaryFile $pharFilenam try { $this->githubCliVerification->verify($releaseMetadata, $pharFilename, $io); } catch (GithubCliNotAvailable $githubCliNotAvailable) { - $io->write($githubCliNotAvailable->getMessage(), verbosity: IOInterface::VERBOSE); + $io->writeError($githubCliNotAvailable->getMessage(), verbosity: IOInterface::VERBOSE); if (! extension_loaded('openssl')) { throw FailedToVerifyRelease::fromNoOpenssl();