Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically setup php.ini entries #155

Merged
merged 18 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
b5e03d7
Extract setup of PHP INI files into new component
asgrim Dec 5, 2024
fa1b2d3
Add assertion to PhpBinaryPath to ensure an ext is loaded in runtime
asgrim Dec 6, 2024
bd32d3f
Added method to check if an extension is already in an INI file
asgrim Dec 6, 2024
f8ce6b7
Added helper to add extension to an INI file
asgrim Dec 9, 2024
75bdc18
Added standard single php.ini approach
asgrim Dec 9, 2024
dd7af3e
Added --skip-enable-extension option to avoid auto enabling of extens…
asgrim Dec 11, 2024
80ba60b
Ensure new TargetPlatform and Package constructor params are used aft…
asgrim Dec 11, 2024
f34e2a5
Check if ext is already loaded before trying to add to any INI files
asgrim Dec 12, 2024
38f2dfd
Added priority property to Package
asgrim Dec 12, 2024
9aacbdc
Extract checking and adding ext to a given INI file into a separate c…
asgrim Dec 12, 2024
d87ca3c
Added processing of the 'additional .ini files' directory to enable e…
asgrim Dec 12, 2024
f331578
Ensure AddExtensionToTheIniFile tests are Windows friendly
asgrim Dec 12, 2024
294a529
Added docker-php-ext-enable INI method
asgrim Dec 17, 2024
e4a56e3
Extract ini file and directory functions into PhpBinaryPath
asgrim Dec 17, 2024
acf9e28
Added priority comment into generated INI content
asgrim Dec 18, 2024
1827440
Add ability for additional step before checking exts are enabled
asgrim Dec 18, 2024
5329ebe
Added INI enable approach for phpenmod
asgrim Dec 20, 2024
8887791
Validate extension really is enabled in PHP
asgrim Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading