Skip to content

Commit 92c45f4

Browse files
committed
WIP Adds configuration mechanism
1 parent 6491be4 commit 92c45f4

File tree

2 files changed

+204
-0
lines changed

2 files changed

+204
-0
lines changed

src/PhpConfigLoader.php

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Stolt\LeanPackage;
6+
7+
final class PhpConfigLoader
8+
{
9+
/**
10+
* Names searched in the current working directory, first match wins.
11+
*
12+
* @var string[]
13+
*/
14+
private const DEFAULT_FILENAMES = [
15+
'.lpv.php',
16+
'.lpv.php.dist',
17+
];
18+
19+
/**
20+
* Discover a configuration file in the current working directory.
21+
*
22+
* @return string|null
23+
*/
24+
public static function discover(): ?string
25+
{
26+
$cwd = \getcwd() ?: '.';
27+
28+
foreach (self::DEFAULT_FILENAMES as $file) {
29+
$path = $cwd . DIRECTORY_SEPARATOR . $file;
30+
31+
if (\is_file($path) && \is_readable($path)) {
32+
return $path;
33+
}
34+
}
35+
36+
return null;
37+
}
38+
39+
/**
40+
* Load a configuration file and validate its structure.
41+
*
42+
* Allowed keys (subset relevant to validate command):
43+
* - directory: string
44+
* - preset: string
45+
* - glob-pattern: string
46+
* - glob-pattern-file: string
47+
* - stdin-input: bool
48+
* - diff: bool
49+
* - report-stale-export-ignores: bool
50+
* - enforce-strict-order: bool
51+
* - enforce-alignment: bool
52+
* - sort-from-directories-to-files: bool
53+
* - keep-license: bool
54+
* - keep-readme: bool
55+
* - keep-glob-pattern: string
56+
* - align-export-ignores: bool
57+
*
58+
* @param string $path
59+
* @return array<string, mixed>
60+
*/
61+
public static function load(string $path): array
62+
{
63+
/** @var mixed $config */
64+
$config = (static function (string $__path) {
65+
/** @noinspection PhpIncludeInspection */
66+
return require $__path;
67+
})($path);
68+
69+
if (!\is_array($config)) {
70+
throw new \UnexpectedValueException('The configuration file must return an array.');
71+
}
72+
73+
$allowed = [
74+
'directory',
75+
'preset',
76+
'glob-pattern',
77+
'glob-pattern-file',
78+
'stdin-input',
79+
'diff',
80+
'report-stale-export-ignores',
81+
'enforce-strict-order',
82+
'enforce-alignment',
83+
'sort-from-directories-to-files',
84+
'keep-license',
85+
'keep-readme',
86+
'keep-glob-pattern',
87+
'align-export-ignores',
88+
];
89+
90+
$unknown = \array_diff(\array_keys($config), $allowed);
91+
if ($unknown !== []) {
92+
throw new \UnexpectedValueException('Unknown configuration keys: ' . \implode(', ', $unknown));
93+
}
94+
95+
// Basic type validation
96+
$stringKeys = ['directory', 'preset', 'glob-pattern', 'glob-pattern-file', 'keep-glob-pattern'];
97+
foreach ($stringKeys as $key) {
98+
if (isset($config[$key]) && !\is_string($config[$key])) {
99+
throw new \UnexpectedValueException(\sprintf('Configuration "%s" must be a string.', $key));
100+
}
101+
}
102+
103+
$boolKeys = [
104+
'stdin-input',
105+
'diff',
106+
'report-stale-export-ignores',
107+
'enforce-strict-order',
108+
'enforce-alignment',
109+
'sort-from-directories-to-files',
110+
'keep-license',
111+
'keep-readme',
112+
'align-export-ignores',
113+
];
114+
foreach ($boolKeys as $key) {
115+
if (isset($config[$key]) && !\is_bool($config[$key])) {
116+
throw new \UnexpectedValueException(\sprintf('Configuration "%s" must be a boolean.', $key));
117+
}
118+
}
119+
120+
return $config;
121+
}
122+
}

tests/PhpConfigLoaderTest.php

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Stolt\LeanPackage\Tests;
6+
7+
use PHPUnit\Framework\Attributes\Test;
8+
use Stolt\LeanPackage\PhpConfigLoader;
9+
10+
final class PhpConfigLoaderTest extends TestCase
11+
{
12+
public function setUp(): void
13+
{
14+
$this->setUpTemporaryDirectory();
15+
}
16+
17+
/**
18+
* Tear down the test environment.
19+
*
20+
* @return void
21+
*/
22+
protected function tearDown(): void
23+
{
24+
if (\is_dir($this->temporaryDirectory)) {
25+
$this->removeDirectory($this->temporaryDirectory);
26+
}
27+
}
28+
29+
#[Test]
30+
public function loadThrowsExpectedExceptionOnNonArrayReturn(): void
31+
{
32+
$file = $this->temporaryDirectory . DIRECTORY_SEPARATOR . 'conf.php';
33+
file_put_contents($file, '<?php return 123;');
34+
35+
$this->expectException(\UnexpectedValueException::class);
36+
$this->expectExceptionMessage('The configuration file must return an array.');
37+
38+
PhpConfigLoader::load($file);
39+
}
40+
41+
#[Test]
42+
public function loadThrowsExpectedExceptionOnUnknownKeys(): void
43+
{
44+
$file = $this->temporaryDirectory . DIRECTORY_SEPARATOR . 'conf.php';
45+
file_put_contents($file, "<?php return ['nope' => true];");
46+
47+
$this->expectException(\UnexpectedValueException::class);
48+
$this->expectExceptionMessage('Unknown configuration keys: nope');
49+
50+
PhpConfigLoader::load($file);
51+
}
52+
53+
#[Test]
54+
public function loadThrowsExpectedExceptionOnInvalidTypes(): void
55+
{
56+
$file = $this->temporaryDirectory . DIRECTORY_SEPARATOR . 'conf.php';
57+
file_put_contents($file, "<?php return ['glob-pattern' => true];");
58+
59+
$this->expectException(\UnexpectedValueException::class);
60+
$this->expectExceptionMessage('Configuration "glob-pattern" must be a string.');
61+
62+
PhpConfigLoader::load($file);
63+
}
64+
65+
#[Test]
66+
public function discoverFindsDefaultFile(): void
67+
{
68+
$cwd = \getcwd() ?: '.';
69+
70+
try {
71+
\chdir($this->temporaryDirectory);
72+
file_put_contents($this->temporaryDirectory . DIRECTORY_SEPARATOR . '.lpv.php.dist', '<?php return [];');
73+
74+
$discovered = PhpConfigLoader::discover();
75+
76+
$this->assertNotNull($discovered);
77+
$this->assertStringEndsWith('.lpv.php.dist', (string) $discovered);
78+
} finally {
79+
\chdir($cwd);
80+
}
81+
}
82+
}

0 commit comments

Comments
 (0)