Skip to content

Commit

Permalink
Merge pull request #155 from asgrim/20-detect-setup-php-ini
Browse files Browse the repository at this point in the history
Automatically setup php.ini entries
  • Loading branch information
asgrim authored Dec 23, 2024
2 parents e213e44 + 8887791 commit dc299c3
Show file tree
Hide file tree
Showing 59 changed files with 3,142 additions and 22 deletions.
2 changes: 2 additions & 0 deletions src/Command/BuildCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
PieOperation::Resolve,
[], // Configure options are not needed for resolve only
null,
false, // setting up INI not needed for build
),
);

Expand All @@ -74,6 +75,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
PieOperation::Build,
$configureOptionsValues,
CommandHelper::determinePhpizePathFromInputs($input),
false, // setting up INI not needed for build
),
);

Expand Down
12 changes: 12 additions & 0 deletions src/Command/CommandHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ final class CommandHelper
private const OPTION_WITH_PHP_PATH = 'with-php-path';
private const OPTION_WITH_PHPIZE_PATH = 'with-phpize-path';
private const OPTION_MAKE_PARALLEL_JOBS = 'make-parallel-jobs';
private const OPTION_SKIP_ENABLE_EXTENSION = 'skip-enable-extension';

/** @psalm-suppress UnusedConstructor */
private function __construct()
Expand Down Expand Up @@ -79,6 +80,12 @@ public static function configureDownloadBuildInstallOptions(Command $command): v
InputOption::VALUE_REQUIRED,
'The path to the `phpize` binary to use as the target PHP platform, e.g. --' . self::OPTION_WITH_PHPIZE_PATH . '=/usr/bin/phpize7.4',
);
$command->addOption(
self::OPTION_SKIP_ENABLE_EXTENSION,
null,
InputOption::VALUE_NONE,
'Specify this to skip attempting to enable the extension in php.ini',
);

self::configurePhpConfigOptions($command);

Expand Down Expand Up @@ -154,6 +161,11 @@ public static function determineTargetPlatformFromInputs(InputInterface $input,
return $targetPlatform;
}

public static function determineAttemptToSetupIniFile(InputInterface $input): bool
{
return ! $input->hasOption(self::OPTION_SKIP_ENABLE_EXTENSION) || ! $input->getOption(self::OPTION_SKIP_ENABLE_EXTENSION);
}

public static function determinePhpizePathFromInputs(InputInterface $input): PhpizePath|null
{
if ($input->hasOption(self::OPTION_WITH_PHPIZE_PATH)) {
Expand Down
1 change: 1 addition & 0 deletions src/Command/DownloadCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
PieOperation::Download,
[], // Configure options are not needed for download only
null,
false, // setting up INI not needed for download
),
);

Expand Down
1 change: 1 addition & 0 deletions src/Command/InfoCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
PieOperation::Resolve,
[], // Configure options are not needed for resolve only
null,
false, // setting up INI not needed for info
),
);

Expand Down
2 changes: 2 additions & 0 deletions src/Command/InstallCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
PieOperation::Resolve,
[], // Configure options are not needed for resolve only
null,
false, // setting up INI not needed for resolve step
),
);

Expand All @@ -79,6 +80,7 @@ public function execute(InputInterface $input, OutputInterface $output): int
PieOperation::Install,
$configureOptionsValues,
CommandHelper::determinePhpizePathFromInputs($input),
CommandHelper::determineAttemptToSetupIniFile($input),
),
);

Expand Down
1 change: 1 addition & 0 deletions src/ComposerIntegration/InstallAndBuildProcess.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public function __invoke(
$downloadedPackage,
$composerRequest->targetPlatform,
$output,
$composerRequest->attemptToSetupIniFile,
),
);
}
Expand Down
1 change: 1 addition & 0 deletions src/ComposerIntegration/PieComposerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public function __construct(
public readonly PieOperation $operation,
public readonly array $configureOptions,
public readonly PhpizePath|null $phpizePath,
public readonly bool $attemptToSetupIniFile,
) {
}
}
14 changes: 14 additions & 0 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
use Php\Pie\DependencyResolver\ResolveDependencyWithComposer;
use Php\Pie\Downloading\GithubPackageReleaseAssets;
use Php\Pie\Downloading\PackageReleaseAssets;
use Php\Pie\Installing\Ini;
use Php\Pie\Installing\Install;
use Php\Pie\Installing\UnixInstall;
use Php\Pie\Installing\WindowsInstall;
Expand Down Expand Up @@ -71,6 +72,19 @@ static function (ContainerInterface $container): Build {
},
);

$container->singleton(
Ini\SetupIniApproach::class,
static function (ContainerInterface $container): Ini\SetupIniApproach {
return new Ini\PickBestSetupIniApproach([
$container->get(Ini\PreCheckExtensionAlreadyLoaded::class),
$container->get(Ini\OndrejPhpenmod::class),
$container->get(Ini\DockerPhpExtEnable::class),
$container->get(Ini\StandardAdditionalPhpIniDirectory::class),
$container->get(Ini\StandardSinglePhpIni::class),
]);
},
);

$container->singleton(
Install::class,
static function (ContainerInterface $container): Install {
Expand Down
2 changes: 2 additions & 0 deletions src/DependencyResolver/Package.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public function __construct(
public readonly string|null $buildPath,
public readonly array|null $compatibleOsFamilies,
public readonly array|null $incompatibleOsFamilies,
public readonly int $priority,
) {
}

Expand Down Expand Up @@ -93,6 +94,7 @@ public static function fromComposerCompletePackage(CompletePackageInterface $com
$buildPath,
self::convertInputStringsToOperatingSystemFamilies($compatibleOsFamilies),
self::convertInputStringsToOperatingSystemFamilies($incompatibleOsFamilies),
$phpExtOptions['priority'] ?? 80,
);
}

Expand Down
102 changes: 102 additions & 0 deletions src/Installing/Ini/AddExtensionToTheIniFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Installing\Ini;

use Php\Pie\DependencyResolver\Package;
use Php\Pie\ExtensionType;
use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;

use function file_get_contents;
use function file_put_contents;
use function is_string;
use function is_writable;
use function sprintf;

use const PHP_EOL;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
class AddExtensionToTheIniFile
{
/** @param callable():bool|null $additionalEnableStep */
public function __invoke(
string $ini,
Package $package,
PhpBinaryPath $phpBinaryPath,
OutputInterface $output,
callable|null $additionalEnableStep,
): bool {
if (! is_writable($ini)) {
$output->writeln(
sprintf(
'PHP is configured to use %s, but it is not writable by PIE.',
$ini,
),
OutputInterface::VERBOSITY_VERBOSE,
);

return false;
}

$originalIniContent = file_get_contents($ini);

if (! is_string($originalIniContent)) {
$output->writeln(
sprintf(
'Tried making a backup of %s but could not read it, aborting enablement of extension',
$ini,
),
OutputInterface::VERBOSITY_VERBOSE,
);

return false;
}

try {
file_put_contents(
$ini,
$originalIniContent . $this->iniFileContent($package),
);
$output->writeln(
sprintf(
'Enabled extension %s in the INI file %s',
$package->extensionName->name(),
$ini,
),
OutputInterface::VERBOSITY_VERBOSE,
);

if ($additionalEnableStep !== null && ! $additionalEnableStep()) {
return false;
}

$phpBinaryPath->assertExtensionIsLoadedInRuntime($package->extensionName, $output);

return true;
} catch (Throwable $anything) {
file_put_contents($ini, $originalIniContent);

$output->writeln(sprintf(
'<error>Something went wrong enabling the %s extension: %s</error>',
$package->extensionName->name(),
$anything->getMessage(),
));

return false;
}
}

/** @return non-empty-string */
private function iniFileContent(Package $package): string
{
return PHP_EOL
. '; PIE automatically added this to enable the ' . $package->name . ' extension' . PHP_EOL
. '; priority=' . $package->priority . PHP_EOL
. ($package->extensionType === ExtensionType::PhpModule ? 'extension' : 'zend_extension')
. '='
. $package->extensionName->name() . PHP_EOL;
}
}
84 changes: 84 additions & 0 deletions src/Installing/Ini/CheckAndAddExtensionToIniIfNeeded.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Installing\Ini;

use Php\Pie\Downloading\DownloadedPackage;
use Php\Pie\Platform\TargetPlatform;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;

use function file_exists;
use function is_readable;
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 */
class CheckAndAddExtensionToIniIfNeeded
{
public function __construct(
private readonly IsExtensionAlreadyInTheIniFile $isExtensionAlreadyInTheIniFile,
private readonly AddExtensionToTheIniFile $addExtensionToTheIniFile,
) {
}

/**
* @param non-empty-string $iniFile
* @param callable():bool|null $additionalEnableStep
*/
public function __invoke(
string $iniFile,
TargetPlatform $targetPlatform,
DownloadedPackage $downloadedPackage,
OutputInterface $output,
callable|null $additionalEnableStep,
): bool {
if (! file_exists($iniFile) || ! is_readable($iniFile)) {
$output->writeln(
sprintf(
'PHP is configured to use %s, but it did not exist, or is not readable by PIE.',
$iniFile,
),
OutputInterface::VERBOSITY_VERBOSE,
);

return false;
}

if (($this->isExtensionAlreadyInTheIniFile)($iniFile, $downloadedPackage->package->extensionName)) {
$output->writeln(
sprintf(
'Extension is already enabled in the INI file %s',
$iniFile,
),
OutputInterface::VERBOSITY_VERBOSE,
);

if ($additionalEnableStep !== null && ! $additionalEnableStep()) {
return false;
}

try {
$targetPlatform->phpBinaryPath->assertExtensionIsLoadedInRuntime($downloadedPackage->package->extensionName, $output);

return true;
} catch (Throwable $anything) {
$output->writeln(sprintf(
'<error>Something went wrong verifying the %s extension is enabled: %s</error>',
$downloadedPackage->package->extensionName->name(),
$anything->getMessage(),
));

return false;
}
}

return ($this->addExtensionToTheIniFile)(
$iniFile,
$downloadedPackage->package,
$targetPlatform->phpBinaryPath,
$output,
$additionalEnableStep,
);
}
}
Loading

0 comments on commit dc299c3

Please sign in to comment.