Skip to content

Commit f75ce59

Browse files
committed
Merge branch 'config'
2 parents 210fff0 + 1a2a2a6 commit f75ce59

File tree

32 files changed

+663
-44
lines changed

32 files changed

+663
-44
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
/phpstan* export-ignore
99
/phpunit* export-ignore
1010
/scripts export-ignore
11+
/src/Toolkit/Core/Facade/Config.php linguist-generated
1112
/src/Util/Cli/CliOptionBuilder.php linguist-generated
1213
/src/Util/Curler/CurlerBuilder.php linguist-generated
1314
/src/Util/Curler/Support/CurlerPageBuilder.php linguist-generated

lk-util/Command/Generate/GenerateFacade.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
use Lkrms\Support\PhpDoc\PhpDoc;
1010
use Lkrms\Utility\Arr;
1111
use Lkrms\Utility\Reflect;
12-
use Salient\Core\Facade;
12+
use Salient\Core\AbstractFacade;
1313
use ReflectionMethod;
1414
use ReflectionParameter;
1515

@@ -24,6 +24,10 @@ final class GenerateFacade extends GenerateCommand
2424
private const SKIP_METHODS = [
2525
'getReadableProperties',
2626
'getWritableProperties',
27+
'offsetExists',
28+
'offsetGet',
29+
'offsetSet',
30+
'offsetUnset',
2731
'withFacade',
2832
'withoutFacade',
2933
// These are displaced by Facade
@@ -123,7 +127,7 @@ protected function run(string ...$args)
123127
$classPrefix = $this->getClassPrefix();
124128

125129
$service = $this->getFqcnAlias($classFqcn, $classClass);
126-
$extends = $this->getFqcnAlias(Facade::class);
130+
$extends = $this->getFqcnAlias(AbstractFacade::class);
127131

128132
$alias = [];
129133
foreach ($aliasFqcn as $aliasFqcn) {

scripts/generate.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,12 +57,15 @@
5757
use Lkrms\Utility\Json;
5858
use Lkrms\Utility\Package;
5959
use Lkrms\Utility\Pcre;
60+
use Salient\Core\Facade\Config;
61+
use Salient\Core\ConfigurationManager;
6062

61-
$loader = require dirname(__DIR__) . '/vendor/autoload.php';
63+
require dirname(__DIR__) . '/vendor/autoload.php';
6264

6365
$facades = [
6466
App::class => [ContainerInterface::class, [Container::class], '--desc', 'A facade for the global service container', '--api'],
6567
Cache::class => CacheStore::class,
68+
Config::class => [ConfigurationManager::class, '--api'],
6669
Console::class => [ConsoleWriter::class, '--api'],
6770
Err::class => [ErrorHandler::class, '--skip', 'handleShutdown,handleError,handleException'],
6871
Event::class => EventDispatcher::class,

src/Toolkit/Core/Facade.php renamed to src/Toolkit/Core/AbstractFacade.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
*
2525
* @implements FacadeInterface<TService>
2626
*/
27-
abstract class Facade implements FacadeInterface
27+
abstract class AbstractFacade implements FacadeInterface
2828
{
2929
/** @use ResolvesServiceLists<TService> */
3030
use ResolvesServiceLists;
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Salient\Core;
4+
5+
use Lkrms\Utility\Arr;
6+
use Lkrms\Utility\File;
7+
use Lkrms\Utility\Pcre;
8+
use Salient\Core\Exception\InvalidConfigurationException;
9+
use ArrayAccess;
10+
use LogicException;
11+
use OutOfRangeException;
12+
use ReturnTypeWillChange;
13+
14+
/**
15+
* Provides access to values in configuration files
16+
*
17+
* @implements ArrayAccess<string,mixed>
18+
*/
19+
final class ConfigurationManager implements ArrayAccess
20+
{
21+
/**
22+
* @var array<string,mixed[]>
23+
*/
24+
private array $Items = [];
25+
26+
/**
27+
* @param array<string,mixed[]> $items
28+
*/
29+
public function __construct(array $items = [])
30+
{
31+
$this->Items = $items;
32+
}
33+
34+
/**
35+
* Load values from files in a directory
36+
*
37+
* @return $this
38+
*/
39+
public function loadDirectory(string $directory): self
40+
{
41+
$items = [];
42+
foreach (File::find()->in($directory)->include('/\.php$/')->doNotRecurse() as $file) {
43+
$basename = $file->getBasename('.php');
44+
$file = (string) $file;
45+
if (Pcre::match('/[\s.]|^[0-9]+$/', $basename)) {
46+
throw new InvalidConfigurationException(sprintf(
47+
'Invalid configuration file name: %s',
48+
$file,
49+
));
50+
}
51+
$values = require $file;
52+
if (!is_array($values)) {
53+
throw new InvalidConfigurationException(sprintf(
54+
'Invalid configuration file: %s',
55+
$file,
56+
));
57+
}
58+
$items[$basename] = $values;
59+
}
60+
ksort($items, \SORT_NATURAL);
61+
$this->Items = $items;
62+
return $this;
63+
}
64+
65+
/**
66+
* Check if a configuration value exists
67+
*/
68+
public function has(string $key): bool
69+
{
70+
return Arr::has($key, $this->Items);
71+
}
72+
73+
/**
74+
* Get a configuration value
75+
*
76+
* @param mixed $default
77+
* @return mixed
78+
* @throws OutOfRangeException if `$key` is not configured and no `$default`
79+
* is given.
80+
*/
81+
public function get(string $key, $default = null)
82+
{
83+
if (func_num_args() > 1) {
84+
return Arr::get($key, $this->Items, $default);
85+
}
86+
return Arr::get($key, $this->Items);
87+
}
88+
89+
/**
90+
* Get multiple configuration values
91+
*
92+
* @param array<string|int,mixed|string> $keys An array that optionally maps
93+
* keys to default values.
94+
* @return array<string,mixed>
95+
* @throws OutOfRangeException if a key is not configured and no default
96+
* value is given.
97+
*/
98+
public function getMany(array $keys): array
99+
{
100+
foreach ($keys as $key => $default) {
101+
if (is_int($key)) {
102+
$key = $default;
103+
$values[$key] = Arr::get($key, $this->Items);
104+
continue;
105+
}
106+
$values[$key] = Arr::get($key, $this->Items, $default);
107+
}
108+
return $values ?? [];
109+
}
110+
111+
/**
112+
* Get all configuration values
113+
*
114+
* @return array<string,mixed[]>
115+
*/
116+
public function all(): array
117+
{
118+
return $this->Items;
119+
}
120+
121+
/**
122+
* @internal
123+
*
124+
* @param string $offset
125+
*/
126+
public function offsetExists($offset): bool
127+
{
128+
return Arr::get($offset, $this->Items, null) !== null;
129+
}
130+
131+
/**
132+
* @internal
133+
*
134+
* @param string $offset
135+
* @return mixed
136+
*/
137+
#[ReturnTypeWillChange]
138+
public function offsetGet($offset)
139+
{
140+
return Arr::get($offset, $this->Items);
141+
}
142+
143+
/**
144+
* @internal
145+
*
146+
* @param string $offset
147+
* @param mixed $value
148+
*/
149+
public function offsetSet($offset, $value): void
150+
{
151+
throw new LogicException('Configuration values are read-only');
152+
}
153+
154+
/**
155+
* @internal
156+
*
157+
* @param string $offset
158+
*/
159+
public function offsetUnset($offset): void
160+
{
161+
throw new LogicException('Configuration values are read-only');
162+
}
163+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace Salient\Core\Exception;
4+
5+
use Lkrms\Exception\Exception;
6+
7+
/**
8+
* @api
9+
*/
10+
class InvalidConfigurationException extends Exception {}

src/Toolkit/Core/Facade/Config.php

Lines changed: 32 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Util/Container/Application.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
use Lkrms\Utility\Pcre;
2727
use Lkrms\Utility\Sys;
2828
use Lkrms\Utility\Test;
29+
use Salient\Core\Facade\Config;
2930
use LogicException;
3031
use Phar;
3132

@@ -264,15 +265,21 @@ final public function getTempPath(bool $create = true): string
264265
* is used after removing common PHP file extensions and recognised version
265266
* numbers.
266267
*
268+
* If `$configDir` exists and is a directory, it is passed to
269+
* {@see Config::loadDirectory()} after `.env` files are loaded and applied.
270+
*
267271
* @api
268272
*
269273
* @param int-mask-of<EnvFlag::*> $envFlags Values to apply from the
270274
* environment to the running script.
275+
* @param string|null $configDir A path relative to the application's base
276+
* path, or `null` if configuration files should not be loaded.
271277
*/
272278
public function __construct(
273279
?string $basePath = null,
274280
?string $appName = null,
275-
int $envFlags = EnvFlag::ALL
281+
int $envFlags = EnvFlag::ALL,
282+
?string $configDir = 'config'
276283
) {
277284
if (!isset(self::$StartTime)) {
278285
self::$StartTime = hrtime(true);
@@ -345,6 +352,15 @@ public function __construct(
345352
if ($adodb !== null) {
346353
Err::silencePath($adodb);
347354
}
355+
356+
if ((string) $configDir !== '') {
357+
if (!File::isAbsolute($configDir)) {
358+
$configDir = "{$this->BasePath}/{$configDir}";
359+
}
360+
if (is_dir($configDir)) {
361+
Config::loadDirectory($configDir);
362+
}
363+
}
348364
}
349365

350366
/**

src/Util/Container/ApplicationInterface.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,8 @@ public function getBasePath(): string;
5050
public function getCachePath(): string;
5151

5252
/**
53-
* Get a writable directory for the application's configuration files
53+
* Get a writable directory for configuration files created by the
54+
* application
5455
*/
5556
public function getConfigPath(): string;
5657

src/Util/Facade/App.php

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)