Skip to content

Commit 5bb9aa2

Browse files
committed
Add the ability to cache installed version
1 parent 78c7a26 commit 5bb9aa2

7 files changed

Lines changed: 184 additions & 4 deletions

File tree

psalm.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
55
xmlns="https://getpsalm.org/schema/config"
66
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
7+
findUnusedCode="false"
8+
findUnusedBaselineEntry="true"
79
>
810
<projectFiles>
911
<directory name="src" />
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RoadRunner\VersionChecker\Environment;
6+
7+
interface EnvironmentInterface
8+
{
9+
/**
10+
* @param non-empty-string $name
11+
*/
12+
public function get(string $name, mixed $default = null): mixed;
13+
}

src/Environment/Native.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RoadRunner\VersionChecker\Environment;
6+
7+
final class Native implements EnvironmentInterface
8+
{
9+
public function __construct(
10+
private array $values = []
11+
) {
12+
$this->values = $values + $_ENV + $_SERVER;
13+
}
14+
15+
/**
16+
* @param non-empty-string $name
17+
*/
18+
public function get(string $name, mixed $default = null): mixed
19+
{
20+
return $this->values[$name] ?? $default;
21+
}
22+
}

src/Version/Installed.php

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,28 @@
44

55
namespace RoadRunner\VersionChecker\Version;
66

7+
use RoadRunner\VersionChecker\Environment\EnvironmentInterface;
8+
use RoadRunner\VersionChecker\Environment\Native;
79
use RoadRunner\VersionChecker\Exception\RoadrunnerNotInstalledException;
810
use RoadRunner\VersionChecker\Process\Process;
911
use RoadRunner\VersionChecker\Process\ProcessInterface;
1012
use Symfony\Component\Process\Exception\ProcessFailedException;
1113

1214
final class Installed implements InstalledInterface
1315
{
16+
private const ENV_VARIABLE = 'RR_VERSION';
17+
18+
/**
19+
* @var non-empty-string|null
20+
*/
21+
private static ?string $cachedVersion = null;
22+
1423
/**
1524
* @param non-empty-string $executablePath
1625
*/
1726
public function __construct(
1827
private readonly ProcessInterface $process = new Process(),
28+
private readonly EnvironmentInterface $environment = new Native(),
1929
private readonly string $executablePath = './rr'
2030
) {
2131
}
@@ -26,6 +36,42 @@ public function __construct(
2636
* @throws RoadrunnerNotInstalledException
2737
*/
2838
public function getInstalledVersion(): string
39+
{
40+
if (!empty(self::$cachedVersion)) {
41+
return self::$cachedVersion;
42+
}
43+
44+
if (!empty(self::$cachedVersion = $this->getVersionFromEnv())) {
45+
return self::$cachedVersion;
46+
}
47+
48+
if (!empty(self::$cachedVersion = $this->getVersionFromConsoleCommand())) {
49+
return self::$cachedVersion;
50+
}
51+
52+
throw new RoadrunnerNotInstalledException('Unable to determine RoadRunner version.');
53+
}
54+
55+
/**
56+
* @return non-empty-string|null
57+
*/
58+
private function getVersionFromEnv(): ?string
59+
{
60+
/** @var string|null $version */
61+
$version = $this->environment->get(self::ENV_VARIABLE);
62+
63+
if (\is_string($version) && !empty($version)) {
64+
return $version;
65+
}
66+
67+
return null;
68+
}
69+
70+
/**
71+
* @return non-empty-string|null
72+
* @throws RoadrunnerNotInstalledException
73+
*/
74+
private function getVersionFromConsoleCommand(): ?string
2975
{
3076
try {
3177
$output = $this->process->exec([$this->executablePath, '--version']);
@@ -35,10 +81,10 @@ public function getInstalledVersion(): string
3581

3682
\preg_match('/\bversion (\d+\.\d+\.\d+[\w.-]*)/', $output, $matches);
3783

38-
if (!empty($matches[1])) {
84+
if (isset($matches[1]) && !empty($matches[1])) {
3985
return $matches[1];
4086
}
4187

42-
throw new RoadrunnerNotInstalledException('Unable to determine RoadRunner version.');
88+
return null;
4389
}
4490
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace RoadRunner\VersionChecker\Tests\Unit\Environment;
6+
7+
use PHPUnit\Framework\TestCase;
8+
use RoadRunner\VersionChecker\Environment\Native;
9+
10+
final class NativeTest extends TestCase
11+
{
12+
/**
13+
* @dataProvider valuesDataProvider
14+
*/
15+
public function testGet(mixed $value, mixed $expected, string $key): void
16+
{
17+
$native = new Native([
18+
'1' => '2.12.3',
19+
'2' => 1,
20+
'3' => true,
21+
]);
22+
23+
$this->assertSame($expected, $native->get($key));
24+
}
25+
26+
27+
public static function valuesDataProvider(): \Traversable
28+
{
29+
yield ['2.12.3', '2.12.3', '1'];
30+
yield [1, 1, '2'];
31+
yield [true, true, '3'];
32+
}
33+
}

tests/src/Unit/Version/ComparatorTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace RoadRunner\VersionChecker\Tests\Unit;
5+
namespace RoadRunner\VersionChecker\Tests\Unit\Version;
66

77
use PHPUnit\Framework\TestCase;
88
use RoadRunner\VersionChecker\Version\Comparator;

tests/src/Unit/Version/InstalledTest.php

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,23 @@
22

33
declare(strict_types=1);
44

5-
namespace RoadRunner\VersionChecker\Tests\Unit;
5+
namespace RoadRunner\VersionChecker\Tests\Unit\Version;
66

77
use PHPUnit\Framework\TestCase;
8+
use RoadRunner\VersionChecker\Environment\EnvironmentInterface;
89
use RoadRunner\VersionChecker\Exception\RoadrunnerNotInstalledException;
910
use RoadRunner\VersionChecker\Process\ProcessInterface;
1011
use RoadRunner\VersionChecker\Version\Installed;
1112
use Symfony\Component\Process\Exception\ProcessFailedException;
1213

1314
final class InstalledTest extends TestCase
1415
{
16+
protected function tearDown(): void
17+
{
18+
// clean the cache
19+
(new \ReflectionProperty(Installed::class, 'cachedVersion'))->setValue(null);
20+
}
21+
1522
/**
1623
* @dataProvider outputDataProvider
1724
*/
@@ -29,6 +36,63 @@ public function testGetInstalledVersion(string $version, string $output): void
2936
$this->assertSame($version, $installed->getInstalledVersion());
3037
}
3138

39+
public function testCachedVersion(): void
40+
{
41+
$env = $this->createMock(EnvironmentInterface::class);
42+
$env
43+
// $this->once() is important for this test!
44+
->expects($this->once())
45+
->method('get')
46+
->with('RR_VERSION')
47+
->willReturn('2023.1.0');
48+
49+
$installed = new Installed(environment: $env);
50+
51+
$version = $installed->getInstalledVersion();
52+
$version2 = $installed->getInstalledVersion();
53+
54+
$this->assertSame('2023.1.0', $version);
55+
$this->assertSame('2023.1.0', $version2);
56+
}
57+
58+
public function getVersionFromEnv(): void
59+
{
60+
$env = $this->createMock(EnvironmentInterface::class);
61+
$env
62+
->expects($this->once())
63+
->method('get')
64+
->with('RR_VERSION')
65+
->willReturn('2023.1.0');
66+
67+
$process = $this->createMock(ProcessInterface::class);
68+
$process->expects($this->never());
69+
70+
$installed = new Installed($process, $env);
71+
72+
$this->assertSame('2023.1.0', $installed->getInstalledVersion());
73+
}
74+
75+
public function getVersionFromConsoleCommand(): void
76+
{
77+
$env = $this->createMock(EnvironmentInterface::class);
78+
$env
79+
->expects($this->once())
80+
->method('get')
81+
->with('RR_VERSION')
82+
->willReturn(null);
83+
84+
$process = $this->createMock(ProcessInterface::class);
85+
$process
86+
->expects($this->once())
87+
->method('exec')
88+
->with(['./rr', '--version'])
89+
->willReturn('version 2023.1.0');
90+
91+
$installed = new Installed($process, $env);
92+
93+
$this->assertSame('2023.1.0', $installed->getInstalledVersion());
94+
}
95+
3296
public function testGetInstalledVersionRoadRunnerIsNotInstalled(): void
3397
{
3498
$process = $this->createMock(ProcessInterface::class);

0 commit comments

Comments
 (0)