Skip to content

Commit

Permalink
Merge pull request #21 from asgrim/install-command-implementation
Browse files Browse the repository at this point in the history
Install command implementation
  • Loading branch information
asgrim authored Aug 13, 2024
2 parents 2dc77ac + 523b8f9 commit 4082b39
Show file tree
Hide file tree
Showing 39 changed files with 950 additions and 37 deletions.
2 changes: 2 additions & 0 deletions bin/pie
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Php\Pie;

use Php\Pie\Command\BuildCommand;
use Php\Pie\Command\DownloadCommand;
use Php\Pie\Command\InstallCommand;
use Symfony\Component\Console\Application;
use Symfony\Component\Console\CommandLoader\ContainerCommandLoader;
use Symfony\Component\Console\Input\InputInterface;
Expand All @@ -23,6 +24,7 @@ $application->setCommandLoader(new ContainerCommandLoader(
[
'download' => DownloadCommand::class,
'build' => BuildCommand::class,
'install' => InstallCommand::class,
]
));
$application->run($container->get(InputInterface::class), $container->get(OutputInterface::class));
67 changes: 67 additions & 0 deletions src/Command/InstallCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Command;

use Php\Pie\Building\Build;
use Php\Pie\DependencyResolver\DependencyResolver;
use Php\Pie\Downloading\DownloadAndExtract;
use Php\Pie\Installing\Install;
use Php\Pie\Platform\TargetPlatform;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

#[AsCommand(
name: 'install',
description: 'Download, build, and install a PIE-compatible PHP extension.',
)]
final class InstallCommand extends Command
{
public function __construct(
private readonly DependencyResolver $dependencyResolver,
private readonly DownloadAndExtract $downloadAndExtract,
private readonly Build $build,
private readonly Install $install,
) {
parent::__construct();
}

public function configure(): void
{
parent::configure();

CommandHelper::configureOptions($this);
}

public function execute(InputInterface $input, OutputInterface $output): int
{
if (! TargetPlatform::isRunningAsRoot()) {
$output->writeln('This command needs elevated privileges, and may prompt you for your password.');
}

$targetPlatform = CommandHelper::determineTargetPlatformFromInputs($input, $output);

$requestedNameAndVersionPair = CommandHelper::requestedNameAndVersionPair($input);

$downloadedPackage = CommandHelper::downloadPackage(
$this->dependencyResolver,
$targetPlatform,
$requestedNameAndVersionPair,
$this->downloadAndExtract,
$output,
);

CommandHelper::bindConfigureOptionsFromPackage($this, $downloadedPackage->package, $input);

$configureOptionsValues = CommandHelper::processConfigureOptionsFromInput($downloadedPackage->package, $input);

($this->build)($downloadedPackage, $targetPlatform, $configureOptionsValues, $output);

($this->install)($downloadedPackage, $targetPlatform, $output);

return Command::SUCCESS;
}
}
16 changes: 16 additions & 0 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Php\Pie\Building\WindowsBuild;
use Php\Pie\Command\BuildCommand;
use Php\Pie\Command\DownloadCommand;
use Php\Pie\Command\InstallCommand;
use Php\Pie\DependencyResolver\DependencyResolver;
use Php\Pie\DependencyResolver\ResolveDependencyWithComposer;
use Php\Pie\Downloading\DownloadAndExtract;
Expand All @@ -30,6 +31,9 @@
use Php\Pie\Downloading\PackageReleaseAssets;
use Php\Pie\Downloading\UnixDownloadAndExtract;
use Php\Pie\Downloading\WindowsDownloadAndExtract;
use Php\Pie\Installing\Install;
use Php\Pie\Installing\UnixInstall;
use Php\Pie\Installing\WindowsInstall;
use Php\Pie\Platform\TargetPhp\ResolveTargetPhpToPlatformRepository;
use Psr\Container\ContainerInterface;
use Symfony\Component\Console\Helper\HelperSet;
Expand All @@ -49,6 +53,7 @@ public static function factory(): ContainerInterface

$container->singleton(DownloadCommand::class);
$container->singleton(BuildCommand::class);
$container->singleton(InstallCommand::class);

$container->singleton(IOInterface::class, static function (ContainerInterface $container): IOInterface {
return new ConsoleIO(
Expand Down Expand Up @@ -126,6 +131,17 @@ static function (ContainerInterface $container): Build {
},
);

$container->singleton(
Install::class,
static function (ContainerInterface $container): Install {
if (Platform::isWindows()) {
return $container->get(WindowsInstall::class);
}

return $container->get(UnixInstall::class);
},
);

return $container;
}
}
3 changes: 3 additions & 0 deletions src/DependencyResolver/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Composer\Package\CompletePackageInterface;
use Php\Pie\ConfigureOption;
use Php\Pie\ExtensionName;
use Php\Pie\ExtensionType;

use function array_key_exists;
use function array_map;
Expand All @@ -23,6 +24,7 @@ final class Package

/** @param list<ConfigureOption> $configureOptions */
public function __construct(
public readonly ExtensionType $extensionType,
public readonly ExtensionName $extensionName,
public readonly string $name,
public readonly string $version,
Expand All @@ -43,6 +45,7 @@ public static function fromComposerCompletePackage(CompletePackageInterface $com
: [];

return new self(
ExtensionType::tryFrom($completePackage->getType()) ?? ExtensionType::PhpModule,
ExtensionName::determineFromComposerPackage($completePackage),
$completePackage->getPrettyName(),
$completePackage->getPrettyVersion(),
Expand Down
5 changes: 2 additions & 3 deletions src/DependencyResolver/ResolveDependencyWithComposer.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@
use Composer\Package\CompletePackageInterface;
use Composer\Package\Version\VersionSelector;
use Composer\Repository\RepositorySet;
use Php\Pie\ExtensionType;
use Php\Pie\Platform\TargetPhp\ResolveTargetPhpToPlatformRepository;
use Php\Pie\Platform\TargetPlatform;

use function in_array;
use function preg_match;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
Expand Down Expand Up @@ -43,8 +43,7 @@ public function __invoke(TargetPlatform $targetPlatform, string $packageName, st
throw UnableToResolveRequirement::fromRequirement($packageName, $requestedVersion);
}

$type = $package->getType();
if (! in_array($type, [Package::TYPE_PHP_MODULE, Package::TYPE_ZEND_EXTENSION])) {
if (! ExtensionType::isValid($package->getType())) {
throw UnableToResolveRequirement::toPhpOrZendExtension($package, $packageName, $requestedVersion);
}

Expand Down
2 changes: 2 additions & 0 deletions src/Downloading/DownloadZip.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
interface DownloadZip
{
public const DOWNLOADED_ZIP_FILENAME = 'downloaded.zip';

/**
* @param non-empty-string $localPath
*
Expand Down
4 changes: 3 additions & 1 deletion src/Downloading/DownloadZipWithGuzzle.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
use function assert;
use function file_put_contents;

use const DIRECTORY_SEPARATOR;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
final class DownloadZipWithGuzzle implements DownloadZip
{
Expand All @@ -36,7 +38,7 @@ public function downloadZipAndReturnLocalPath(RequestInterface $request, string

AssertHttp::responseStatusCode(200, $response);

$tmpZipFile = $localPath . '/downloaded.zip';
$tmpZipFile = $localPath . DIRECTORY_SEPARATOR . DownloadZip::DOWNLOADED_ZIP_FILENAME;
file_put_contents($tmpZipFile, $response->getBody()->__toString());

return $tmpZipFile;
Expand Down
33 changes: 2 additions & 31 deletions src/Downloading/GithubPackageReleaseAssets.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,14 @@
use GuzzleHttp\Psr7\Request;
use GuzzleHttp\RequestOptions;
use Php\Pie\DependencyResolver\Package;
use Php\Pie\Downloading\Exception\CouldNotFindReleaseAsset;
use Php\Pie\Platform\OperatingSystem;
use Php\Pie\Platform\TargetPlatform;
use Php\Pie\Platform\WindowsExtensionAssetName;
use Psl\Json;
use Psl\Type;
use Psr\Http\Message\ResponseInterface;

use function assert;
use function in_array;
use function sprintf;
use function strtolower;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
Expand Down Expand Up @@ -47,34 +45,7 @@ public function findWindowsDownloadUrlForPackage(TargetPlatform $targetPlatform,
/** @return non-empty-list<non-empty-string> */
private function expectedWindowsAssetNames(TargetPlatform $targetPlatform, Package $package): array
{
if ($targetPlatform->operatingSystem !== OperatingSystem::Windows || $targetPlatform->windowsCompiler === null) {
throw CouldNotFindReleaseAsset::forMissingWindowsCompiler($targetPlatform);
}

/**
* During development, we swapped compiler/ts around. It is fairly trivial to support both, so we can check
* both formats pretty easily, just to avoid confusion for package maintainers...
*/
return [
strtolower(sprintf(
'php_%s-%s-%s-%s-%s-%s.zip',
$package->extensionName->name(),
$package->version,
$targetPlatform->phpBinaryPath->majorMinorVersion(),
$targetPlatform->threadSafety->asShort(),
strtolower($targetPlatform->windowsCompiler->name),
$targetPlatform->architecture->name,
)),
strtolower(sprintf(
'php_%s-%s-%s-%s-%s-%s.zip',
$package->extensionName->name(),
$package->version,
$targetPlatform->phpBinaryPath->majorMinorVersion(),
strtolower($targetPlatform->windowsCompiler->name),
$targetPlatform->threadSafety->asShort(),
$targetPlatform->architecture->name,
)),
];
return WindowsExtensionAssetName::zipNames($targetPlatform, $package);
}

/** @link https://github.com/squizlabs/PHP_CodeSniffer/issues/3734 */
Expand Down
17 changes: 17 additions & 0 deletions src/ExtensionType.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

declare(strict_types=1);

namespace Php\Pie;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
enum ExtensionType: string
{
case PhpModule = 'php-ext';
case ZendExtension = 'php-ext-zend';

public static function isValid(string $toBeChecked): bool
{
return self::tryFrom($toBeChecked) !== null;
}
}
25 changes: 25 additions & 0 deletions src/Installing/Install.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Installing;

use Php\Pie\Downloading\DownloadedPackage;
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
{
/**
* Install the extension in the given target platform's PHP, and return the location of the installed shared object
* or DLL, depending on the platform implementation.
*
* @return non-empty-string
*/
public function __invoke(
DownloadedPackage $downloadedPackage,
TargetPlatform $targetPlatform,
OutputInterface $output,
): string;
}
52 changes: 52 additions & 0 deletions src/Installing/UnixInstall.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Installing;

use Php\Pie\Downloading\DownloadedPackage;
use Php\Pie\ExtensionType;
use Php\Pie\Platform\TargetPlatform;
use RuntimeException;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Process\Process;

use function file_exists;
use function sprintf;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
final class UnixInstall implements Install
{
public function __invoke(DownloadedPackage $downloadedPackage, TargetPlatform $targetPlatform, OutputInterface $output): string
{
(new Process(['sudo', 'make', 'install'], $downloadedPackage->extractedSourcePath))
->mustRun()
->getOutput();

$sharedObjectName = $downloadedPackage->package->extensionName->name() . '.so';
$expectedSharedObjectLocation = sprintf(
'%s/%s',
$targetPlatform->phpBinaryPath->extensionPath(),
$sharedObjectName,
);

if (! file_exists($expectedSharedObjectLocation)) {
throw new RuntimeException('Install failed, ' . $expectedSharedObjectLocation . ' was not installed.');
}

$output->writeln('<info>Install complete:</info> ' . $expectedSharedObjectLocation);

/**
* @link https://github.com/php/pie/issues/20
*
* @todo this should be improved in future to try to automatically set up the ext
*/
$output->writeln(sprintf(
'<comment>You must now add "%s=%s" to your php.ini</comment>',
$downloadedPackage->package->extensionType === ExtensionType::PhpModule ? 'extension' : 'zend_extension',
$downloadedPackage->package->extensionName->name(),
));

return $expectedSharedObjectLocation;
}
}
Loading

0 comments on commit 4082b39

Please sign in to comment.