Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,39 @@ $value = $config->get('a.b.c');
```
[dflydev/dot-access-data](https://github.com/dflydev/dot-access-data) is leveraged to provide this capability.

### Configuration Overlays

Optionally, you may use the ConfigOverlay class to combine multiple configuration objects implamenting ConfigInterface into a single, prioritized configuration object. It is not necessary to use a configuration overlay; if your only goal is to merge configuration from multiple files, you may follow the example above to extend a processor with multiple configuration files, and then import the result into a single configuration object. This will cause newer configuration items to overwrite any existing values stored under the same key.

A configuration overlay can achieve the same end result without overwriting any config values. The advantage of doing this is that different configuration overlays could be used to create separate "views" on different collections of configuration. A configuration overlay is also useful if you wish to temporarily override some configuration values, and then put things back the way they were by removing the overlay.
```
use Consolidation\Config\Config;
use Consolidation\Config\ConfigOverlay;
use Consolidation\Config\YamlConfigLoader;
use Consolidation\Config\ConfigProcessor;

$config1 = new Config();
$config2 = new Config();
$loader = new YamlConfigLoader();
$processor = new ConfigProcessor();
$processor->extend($loader->load('c1.yml'));
$config1->import($processor->export());
$processor = new ConfigProcessor();
$processor->extend($loader->load('c2.yml'));
$config2->import($processor->export());

$configOverlay = (new ConfigOverlay())
->addContext('one', $config1)
->addContext('two', $config2);

$value = $configOverlay->get('key');

$configOverlay->removeContext('two');

$value = $configOverlay->get('key');
```
The first call to `$configOverlay->get('key')`, above, will return the value from `key` in `$config2`, if it exists, or from `$config1` otherwise. The second call to the same function, after `$config2` is removed, will only consider configuration values stored in `$config1`.

## External Examples

### Load Configuration Files with Symfony/Config
Expand Down
8 changes: 4 additions & 4 deletions src/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ public function has($key)
/**
* {@inheritdoc}
*/
public function get($key, $defaultOverride = null)
public function get($key, $defaultFallback = null)
{
if ($this->has($key)) {
return $this->config->get($key);
}
return $this->getDefault($key, $defaultOverride);
return $this->getDefault($key, $defaultFallback);
}

/**
Expand Down Expand Up @@ -84,9 +84,9 @@ public function hasDefault($key)
/**
* {@inheritdoc}
*/
public function getDefault($key, $defaultOverride = null)
public function getDefault($key, $defaultFallback = null)
{
return $this->hasDefault($key) ? $this->defaults[$key] : $defaultOverride;
return $this->hasDefault($key) ? $this->defaults[$key] : $defaultFallback;
}

/**
Expand Down
11 changes: 7 additions & 4 deletions src/ConfigInterface.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,14 @@ public function has($key);
* Fetch a configuration value
*
* @param string $key Which config item to look up
* @param string|null $defaultOverride Override usual default value with a different default. Deprecated; provide defaults to the config processor instead.
* @param string|null $defaultFallback Fallback default value to use when
* configuration object has neither a value nor a default. Use is
* discouraged; use default context in ConfigOverlay, or provide defaults
* using a config processor.
*
* @return mixed
*/
public function get($key, $defaultOverride = null);
public function get($key, $defaultFallback = null);

/**
* Set a config value
Expand Down Expand Up @@ -55,11 +58,11 @@ public function hasDefault($key);
* Return the default value for a given configuration item.
*
* @param string $key
* @param mixed $defaultOverride
* @param mixed $defaultFallback
*
* @return mixed
*/
public function getDefault($key, $defaultOverride = null);
public function getDefault($key, $defaultFallback = null);

/**
* Set the default value for a configuration setting. This allows us to
Expand Down
176 changes: 176 additions & 0 deletions src/ConfigOverlay.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
<?php
namespace Consolidation\Config;

/**
* Overlay different configuration objects that implement ConfigInterface
* to make a priority-based, merged configuration object.
*
* Note that using a ConfigOverlay hides the defaults stored in each
* individual configuration context. When using overlays, always call
* getDefault / setDefault on the ConfigOverlay object itself.
*/
class ConfigOverlay implements ConfigInterface
{
protected $contexts = [];

const DEFAULT_CONTEXT = 'default';
const PROCESS_CONTEXT = 'process';

public function __construct()
{
$this->contexts[self::DEFAULT_CONTEXT] = new Config();
$this->contexts[self::PROCESS_CONTEXT] = new Config();
}

/**
* Add a named configuration object to the configuration overlay.
* Configuration objects added LAST have HIGHEST priority, with the
* exception of the fact that the process context always has the
* highest priority.
*
* If a context has already been added, its priority will not change.
*/
public function addContext($name, ConfigInterface $config)
{
$process = $this->contexts[self::PROCESS_CONTEXT];
unset($this->contexts[self::PROCESS_CONTEXT]);
$this->contexts[$name] = $config;
$this->contexts[self::PROCESS_CONTEXT] = $process;

return $this;
}

/**
* Add a placeholder context that will be prioritized higher than
* existing contexts. This is done to ensure that contexts added
* later will maintain a higher priority if the placeholder context
* is later relaced with a different configuration set via addContext().
*
* @param string $name
* @return $this
*/
public function addPlaceholder($name)
{
return $this->addContext($name, new Config());
}

/**
* Increase the priority of the named context such that it is higher
* in priority than any existing context except for the 'process'
* context.
*
* @param string $name
* @return $this
*/
public function increasePriority($name)
{
$config = $this->getContext($name);
unset($this->contexts[$name]);
return $this->addContext($name, $config);
}

public function hasContext($name)
{
return isset($this->contexts[$name]);
}

public function getContext($name)
{
if ($this->hasContext($name)) {
return $this->contexts[$name];
}
return new Config();
}

public function removeContext($name)
{
unset($this->contexts[$name]);
}

/**
* Determine if a non-default config value exists.
*/
public function findContext($key)
{
foreach (array_reverse($this->contexts) as $name => $config) {
if ($config->has($key)) {
return $config;
}
}
return false;
}

/**
* @inheritdoc
*/
public function has($key)
{
return $this->findContext($key) != false;
}

/**
* @inheritdoc
*/
public function get($key, $default = null)
{
$context = $this->findContext($key);
if ($context) {
return $context->get($key, $default);
}
return $default;
}

/**
* @inheritdoc
*/
public function set($key, $value)
{
$this->contexts[self::PROCESS_CONTEXT]->set($key, $value);
return $this;
}

/**
* @inheritdoc
*/
public function import($data)
{
throw new \Exception('The method "import" is not supported for the ConfigOverlay class.');
}

/**
* @inheritdoc
*/
public function export()
{
$export = [];
foreach ($this->contexts as $name => $config) {
$export = array_merge_recursive($export, $config->export());
}
return $export;
}

/**
* @inheritdoc
*/
public function hasDefault($key)
{
return $this->contexts[self::DEFAULT_CONTEXT]->has($key);
}

/**
* @inheritdoc
*/
public function getDefault($key, $default = null)
{
return $this->contexts[self::DEFAULT_CONTEXT]->get($key, $default);
}

/**
* @inheritdoc
*/
public function setDefault($key, $value)
{
$this->contexts[self::DEFAULT_CONTEXT]->set($key, $value);
return $this;
}
}
Loading