Skip to content

Commit c6181e1

Browse files
committed
Add extensions list and custom sfx pathname support
1 parent 3747c8c commit c6181e1

File tree

8 files changed

+428
-22
lines changed

8 files changed

+428
-22
lines changed

resources/boson.schema.json

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,23 @@
109109
}
110110
}
111111
},
112+
"extensions": {
113+
"type": "array",
114+
"description": "List of required compile target PHP extensions",
115+
"items": {
116+
"type": "string",
117+
"minLength": 1,
118+
"description": "Name of required compile target PHP extension"
119+
}
120+
},
112121
"ini": {
113122
"type": "object",
114-
"description": "List of additional php.ini variables",
123+
"description": "List of additional compile target php.ini config variables",
115124
"additionalProperties": false,
116125
"patternProperties": {
117126
"^.+$": {
118127
"$ref": "#/$defs/ini-value",
119-
"description": "Additional php.ini key"
128+
"description": "Additional compile target php.ini config value"
120129
}
121130
}
122131
},
@@ -170,15 +179,15 @@
170179
},
171180
"ini-string-value": {
172181
"type": "string",
173-
"description": "Additional php.ini string value"
182+
"description": "Additional compile target php.ini string value"
174183
},
175184
"ini-number-value": {
176185
"type": "number",
177-
"description": "Additional php.ini number (int or float) value"
186+
"description": "Additional compile target php.ini number (int or float) value"
178187
},
179188
"ini-boolean-value": {
180189
"type": "boolean",
181-
"description": "Additional php.ini boolean value"
190+
"description": "Additional compile target php.ini boolean value"
182191
}
183192
}
184193
}

src/Configuration.php

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -52,21 +52,28 @@ final class Configuration
5252
*
5353
* @var list<IncludeConfiguration>
5454
*/
55-
public private(set) array $build;
55+
public private(set) array $build = [];
5656

5757
/**
5858
* PHP INI settings to be applied during compilation.
5959
*
6060
* @var array<non-empty-string, scalar>
6161
*/
62-
public private(set) array $ini;
62+
public private(set) array $ini = [];
6363

6464
/**
6565
* List of mount directories.
6666
*
6767
* @var list<non-empty-string>
6868
*/
69-
public private(set) array $mount;
69+
public private(set) array $mount = [];
70+
71+
/**
72+
* List of required compile target PHP extensions.
73+
*
74+
* @var list<non-empty-string>
75+
*/
76+
public private(set) array $extensions = [];
7077

7178
/**
7279
* List of compilation targets.
@@ -195,9 +202,6 @@ final class Configuration
195202
}
196203

197204
/**
198-
* @param iterable<mixed, IncludeConfiguration> $build
199-
* @param iterable<non-empty-string, scalar> $ini
200-
* @param iterable<mixed, non-empty-string> $mount
201205
* @param non-empty-string|null $output
202206
* @param non-empty-string|null $root
203207
*/
@@ -216,14 +220,8 @@ public function __construct(
216220
public private(set) string $boxVersion = self::DEFAULT_BOX_VERSION,
217221
?string $output = self::DEFAULT_BUILD_DIRECTORY,
218222
?string $root = self::DEFAULT_APP_DIRECTORY,
219-
iterable $build = [],
220-
iterable $ini = [],
221-
iterable $mount = [],
222223
public private(set) int $timestamp = \PHP_INT_MAX,
223224
) {
224-
$this->build = \iterator_to_array($build, false);
225-
$this->ini = \iterator_to_array($ini, true);
226-
$this->mount = \iterator_to_array($mount, false);
227225
$this->output = $output;
228226
$this->root = $root;
229227
}
@@ -349,6 +347,20 @@ public function withAddedTarget(TargetInterface $target): self
349347
return $self;
350348
}
351349

350+
/**
351+
* Returns copy of an instance with an additional required compilation
352+
* target PHP extension.
353+
*
354+
* @param non-empty-string $extension
355+
*/
356+
public function withAddedExtension(string $extension): self
357+
{
358+
$self = clone $this;
359+
$self->extensions[] = $extension;
360+
361+
return $self;
362+
}
363+
352364
/**
353365
* Returns copy of an instance with an additional config's timestamp.
354366
*/

src/Configuration/Factory/JsonConfigurationFactory.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
* output?: non-empty-string,
3939
* root?: non-empty-string,
4040
* mount?: list<non-empty-string>,
41+
* extensions?: list<non-empty-string>,
4142
* build?: RawBuildConfigurationType,
4243
* ini?: array<non-empty-string, string|int|float|bool>,
4344
* box-version?: non-empty-string,
@@ -94,6 +95,7 @@ public function createConfiguration(Configuration $config): Configuration
9495
$config = $this->extendBuildConfiguration($input, $config);
9596
$config = $this->extendIni($input, $config);
9697
$config = $this->extendMount($input, $config);
98+
$config = $this->extendExtension($input, $config);
9799
$config = $this->extendCompilationTargets($input, $config);
98100

99101
return $config;
@@ -283,6 +285,22 @@ private function extendIni(array $input, Configuration $config): Configuration
283285
return $config;
284286
}
285287

288+
/**
289+
* @param RawConfigurationType $input
290+
*/
291+
private function extendExtension(array $input, Configuration $config): Configuration
292+
{
293+
if (!isset($input['extensions'])) {
294+
return $config;
295+
}
296+
297+
foreach ((array) $input['extensions'] as $extension) {
298+
$config = $config->withAddedExtension($extension);
299+
}
300+
301+
return $config;
302+
}
303+
286304
/**
287305
* @param RawConfigurationType $input
288306
*/

src/Target/BuiltinTarget.php

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@
1414

1515
abstract readonly class BuiltinTarget extends Target
1616
{
17+
/**
18+
* @var non-empty-list<non-empty-lowercase-string>
19+
*/
20+
private const array REQUIRED_EXTENSIONS = [
21+
'ffi',
22+
'phar',
23+
];
24+
1725
/**
1826
* @param non-empty-string $type
1927
* @param non-empty-string|null $output
@@ -32,6 +40,79 @@ public function __construct(
3240
);
3341
}
3442

43+
/**
44+
* @return non-empty-string|null
45+
*/
46+
protected function findCustomSfxPathname(Configuration $config): ?string
47+
{
48+
$sfx = $this->config['sfx'] ?? null;
49+
50+
if (!isset($sfx)) {
51+
return null;
52+
}
53+
54+
if (!\is_string($sfx) || $sfx === '') {
55+
throw new \InvalidArgumentException(\sprintf(
56+
'Custom SFX of %s compilation target must be a non empty string, %s given',
57+
$this->type,
58+
\get_debug_type($sfx),
59+
));
60+
}
61+
62+
if (\is_readable($sfx)) {
63+
return $sfx;
64+
}
65+
66+
if (\is_readable($resolved = $config->root . \DIRECTORY_SEPARATOR . $sfx)) {
67+
return $resolved;
68+
}
69+
70+
throw new \InvalidArgumentException(\sprintf(
71+
'Custom SFX "%s" of %s compilation target must be a valid pathname to the file',
72+
$sfx,
73+
$this->type,
74+
));
75+
}
76+
77+
/**
78+
* @param list<non-empty-string> $actual
79+
*/
80+
protected function isExtensionMatches(Configuration $config, array $actual): bool
81+
{
82+
$actual = \array_unique($actual);
83+
84+
foreach ($config->extensions as $expected) {
85+
if (!\in_array(\strtolower($expected), $actual, true)) {
86+
return false;
87+
}
88+
}
89+
90+
return true;
91+
}
92+
93+
/**
94+
* @param list<non-empty-string> $actual
95+
*
96+
* @return list<non-empty-string>
97+
*/
98+
protected function getMissingDependencies(Configuration $config, array $actual): array
99+
{
100+
$actual = \array_unique($actual);
101+
102+
return \array_values(\array_diff($config->extensions, $actual));
103+
}
104+
105+
/**
106+
* @return list<non-empty-string>
107+
*/
108+
protected function getExpectedDependencies(Configuration $config): array
109+
{
110+
return \array_values(\array_unique([
111+
...self::REQUIRED_EXTENSIONS,
112+
...$config->extensions,
113+
]));
114+
}
115+
35116
protected function process(Configuration $config): iterable
36117
{
37118
yield from new ValidatePharAction($this)

src/Target/LinuxBuiltinTarget.php

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,62 @@
2020
*/
2121
private const string DEFAULT_RUNTIME_ARM64_BINARY_NAME = 'libboson-linux-aarch64.so';
2222

23+
/**
24+
* @var list<non-empty-lowercase-string>
25+
*/
26+
private const array MINIMAL_SFX_EXTENSIONS = [
27+
'core',
28+
'ctype',
29+
'date',
30+
'ffi',
31+
'hash',
32+
'iconv',
33+
'json',
34+
'pcre',
35+
'random',
36+
'reflection',
37+
'shmop',
38+
'spl',
39+
'standard',
40+
'zlib',
41+
'mbstring',
42+
'phar',
43+
'opcache',
44+
];
45+
46+
/**
47+
* @var list<non-empty-lowercase-string>
48+
*/
49+
public const array STANDARD_SFX_EXTENSIONS = [
50+
'core',
51+
'ctype',
52+
'curl',
53+
'date',
54+
'ffi',
55+
'hash',
56+
'iconv',
57+
'json',
58+
'openssl',
59+
'pcre',
60+
'random',
61+
'reflection',
62+
'shmop',
63+
'sockets',
64+
'standard',
65+
'spl',
66+
'sqlite3',
67+
'sodium',
68+
'zlib',
69+
'libxml',
70+
'dom',
71+
'mbstring',
72+
'pdo',
73+
'pdo_sqlite',
74+
'phar',
75+
'xml',
76+
'opcache',
77+
];
78+
2379
protected function getRuntimeBinaryFilename(): string
2480
{
2581
return match ($this->arch) {
@@ -35,10 +91,34 @@ protected function getRuntimeBinaryFilename(): string
3591

3692
protected function getSfxArchivePathname(Configuration $config): string
3793
{
94+
if (($sfx = $this->findCustomSfxPathname($config)) !== null) {
95+
return $sfx;
96+
}
97+
3898
return match ($this->arch) {
39-
BuiltinArchitectureTarget::Amd64 => __DIR__ . '/../../bin/minimal/linux-x86_64.sfx',
99+
BuiltinArchitectureTarget::Amd64 => match (true) {
100+
$this->isExtensionMatches($config, self::MINIMAL_SFX_EXTENSIONS)
101+
=> __DIR__ . '/../../bin/minimal/linux-x86_64.sfx',
102+
$this->isExtensionMatches($config, self::STANDARD_SFX_EXTENSIONS)
103+
=> __DIR__ . '/../../bin/standard/linux-x86_64.sfx',
104+
default => throw $this->missingExtensionsError(
105+
config: $config,
106+
actual: self::STANDARD_SFX_EXTENSIONS,
107+
platform: 'linux-x86_64-glibc',
108+
),
109+
},
40110
/** @phpstan-ignore-next-line : Allow invalid architecture arm */
41-
BuiltinArchitectureTarget::Arm64 => __DIR__ . '/../../bin/minimal/linux-aarch64.sfx',
111+
BuiltinArchitectureTarget::Arm64 => match (true) {
112+
$this->isExtensionMatches($config, self::MINIMAL_SFX_EXTENSIONS)
113+
=> __DIR__ . '/../../bin/minimal/linux-aarch64.sfx',
114+
$this->isExtensionMatches($config, self::STANDARD_SFX_EXTENSIONS)
115+
=> __DIR__ . '/../../bin/standard/linux-aarch64.sfx',
116+
default => throw $this->missingExtensionsError(
117+
config: $config,
118+
actual: self::STANDARD_SFX_EXTENSIONS,
119+
platform: 'linux-aarch64-glibc',
120+
),
121+
},
42122
default => throw $this->unsupportedArchitectureOfPlatform(
43123
platform: BuiltinPlatformTarget::Linux,
44124
arch: $this->arch,

0 commit comments

Comments
 (0)