Skip to content

Commit

Permalink
Added docker-php-ext-enable INI method
Browse files Browse the repository at this point in the history
  • Loading branch information
asgrim committed Dec 17, 2024
1 parent cd8044b commit 8e537ba
Show file tree
Hide file tree
Showing 5 changed files with 272 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ static function (ContainerInterface $container): Build {
static function (ContainerInterface $container): Ini\SetupIniApproach {
return new Ini\PickBestSetupIniApproach([
$container->get(Ini\PreCheckExtensionAlreadyLoaded::class),
$container->get(Ini\DockerPhpExtEnable::class),
$container->get(Ini\StandardAdditionalPhpIniDirectory::class),
$container->get(Ini\StandardSinglePhpIni::class),
]);
Expand Down
89 changes: 89 additions & 0 deletions src/Installing/Ini/DockerPhpExtEnable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<?php

declare(strict_types=1);

namespace Php\Pie\Installing\Ini;

use Php\Pie\BinaryFile;
use Php\Pie\Downloading\DownloadedPackage;
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;

/** @internal This is not public API for PIE, so should not be depended upon unless you accept the risk of BC breaks */
final class DockerPhpExtEnable implements SetupIniApproach
{
private const DOCKER_PHP_EXT_ENABLE = 'docker-php-ext-enable';

public function __construct(private readonly string $dockerPhpExtEnableName = self::DOCKER_PHP_EXT_ENABLE)
{
}

public function canBeUsed(TargetPlatform $targetPlatform): bool
{
return $this->dockerPhpExtEnablePath() !== null;
}

public function setup(
TargetPlatform $targetPlatform,
DownloadedPackage $downloadedPackage,
BinaryFile $binaryFile,
OutputInterface $output,
): bool {
$dockerPhpExtEnable = $this->dockerPhpExtEnablePath();

if ($dockerPhpExtEnable === null) {
return false;
}

try {
$enableOutput = Process::run([$dockerPhpExtEnable, $downloadedPackage->package->extensionName->name()]);
} catch (ProcessFailedException $processFailed) {
$output->writeln(
sprintf(
'Could not enable extension %s using %s. Exception was: %s',
$downloadedPackage->package->extensionName->name(),
$this->dockerPhpExtEnableName,
$processFailed->getMessage(),
),
OutputInterface::VERBOSITY_VERBOSE,
);

return false;
}

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

return true;
} catch (ExtensionIsNotLoaded) {
$output->writeln(
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,
);

return false;
}
}

private function dockerPhpExtEnablePath(): string|null
{
try {
return Process::run(['which', $this->dockerPhpExtEnableName]);
} catch (ProcessFailedException) {
return null;
}
}
}
4 changes: 4 additions & 0 deletions test/assets/docker-php-ext-enable/bad
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

echo "something bad happened"
exit 1
4 changes: 4 additions & 0 deletions test/assets/docker-php-ext-enable/good
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash

echo "hi"
exit 0
174 changes: 174 additions & 0 deletions test/unit/Installing/Ini/DockerPhpExtEnableTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
<?php

declare(strict_types=1);

namespace Php\PieUnitTest\Installing\Ini;

use Composer\Package\CompletePackageInterface;
use Php\Pie\BinaryFile;
use Php\Pie\DependencyResolver\Package;
use Php\Pie\Downloading\DownloadedPackage;
use Php\Pie\ExtensionName;
use Php\Pie\ExtensionType;
use Php\Pie\Installing\Ini\DockerPhpExtEnable;
use Php\Pie\Platform\Architecture;
use Php\Pie\Platform\OperatingSystem;
use Php\Pie\Platform\OperatingSystemFamily;
use Php\Pie\Platform\TargetPhp\Exception\ExtensionIsNotLoaded;
use Php\Pie\Platform\TargetPhp\PhpBinaryPath;
use Php\Pie\Platform\TargetPlatform;
use Php\Pie\Platform\ThreadSafetyMode;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Symfony\Component\Console\Output\BufferedOutput;

#[CoversClass(DockerPhpExtEnable::class)]
final class DockerPhpExtEnableTest extends TestCase
{
private const NON_EXISTENT_DOCKER_PHP_EXT_ENABLE = 'something-that-should-not-be-in-path';
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 PhpBinaryPath&MockObject $mockPhpBinary;
private TargetPlatform $targetPlatform;
private DownloadedPackage $downloadedPackage;
private BinaryFile $binaryFile;

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

$this->output = new BufferedOutput(BufferedOutput::VERBOSITY_VERBOSE);

$this->mockPhpBinary = $this->createMock(PhpBinaryPath::class);
/**
* @psalm-suppress PossiblyNullFunctionCall
* @psalm-suppress UndefinedThisPropertyAssignment
*/
(fn () => $this->phpBinaryPath = '/path/to/php')
->bindTo($this->mockPhpBinary, PhpBinaryPath::class)();

$this->targetPlatform = new TargetPlatform(
OperatingSystem::NonWindows,
OperatingSystemFamily::Linux,
$this->mockPhpBinary,
Architecture::x86_64,
ThreadSafetyMode::ThreadSafe,
1,
null,
);

$this->downloadedPackage = DownloadedPackage::fromPackageAndExtractedPath(
new Package(
$this->createMock(CompletePackageInterface::class),
ExtensionType::PhpModule,
ExtensionName::normaliseFromString('foobar'),
'foo/bar',
'1.2.3',
null,
[],
true,
true,
null,
null,
null,
99,
),
'/path/to/extracted/source',
);

$this->binaryFile = new BinaryFile('/path/to/compiled/extension.so', 'fake checksum');
}

public function testCannotBeUsedWhenDockerPhpExtEnableIsNotInPath(): void
{
self::assertFalse(
(new DockerPhpExtEnable(self::NON_EXISTENT_DOCKER_PHP_EXT_ENABLE))
->canBeUsed($this->targetPlatform),
);
}

public function testCanBeUsedWhenDockerPhpExtEnableIsInPath(): void
{
self::assertTrue(
(new DockerPhpExtEnable(self::GOOD_DOCKER_PHP_EXT_ENABLE))
->canBeUsed($this->targetPlatform),
);
}

public function testSetupReturnsFalseWhenWhenDockerPhpExtEnableIsNotInPath(): void
{
$this->mockPhpBinary
->expects(self::never())
->method('assertExtensionIsLoadedInRuntime');

self::assertFalse(
(new DockerPhpExtEnable(self::NON_EXISTENT_DOCKER_PHP_EXT_ENABLE))
->setup(
$this->targetPlatform,
$this->downloadedPackage,
$this->binaryFile,
$this->output,
),
);
}

public function testReturnsTrueWhenDockerPhpExtEnableSuccessfullyEnablesExtension(): void
{
$this->mockPhpBinary
->expects(self::once())
->method('assertExtensionIsLoadedInRuntime')
->with($this->downloadedPackage->package->extensionName, $this->output);

self::assertTrue(
(new DockerPhpExtEnable(self::GOOD_DOCKER_PHP_EXT_ENABLE))
->setup(
$this->targetPlatform,
$this->downloadedPackage,
$this->binaryFile,
$this->output,
),
);
}

public function testReturnsFalseWhenDockerPhpExtEnableFailsToBeRun(): void
{
$this->mockPhpBinary
->expects(self::never())
->method('assertExtensionIsLoadedInRuntime');

self::assertFalse(
(new DockerPhpExtEnable(self::BAD_DOCKER_PHP_EXT_ENABLE))
->setup(
$this->targetPlatform,
$this->downloadedPackage,
$this->binaryFile,
$this->output,
),
);
}

public function testReturnsFalseWhenDockerPhpExtEnableFailsToAssertExtensionWasEnabled(): void
{
$this->mockPhpBinary
->expects(self::once())
->method('assertExtensionIsLoadedInRuntime')
->with($this->downloadedPackage->package->extensionName, $this->output)
->willThrowException(ExtensionIsNotLoaded::fromExpectedExtension(
$this->mockPhpBinary,
$this->downloadedPackage->package->extensionName,
));

self::assertFalse(
(new DockerPhpExtEnable(self::GOOD_DOCKER_PHP_EXT_ENABLE))
->setup(
$this->targetPlatform,
$this->downloadedPackage,
$this->binaryFile,
$this->output,
),
);
}
}

0 comments on commit 8e537ba

Please sign in to comment.