default services conflit ?

This commit is contained in:
armansansd
2022-04-27 11:30:43 +02:00
parent 28190a5749
commit 8bb1064a3b
8132 changed files with 900138 additions and 426 deletions

View File

@@ -0,0 +1,122 @@
<?php
namespace Consolidation\Config\Util;
/**
* Useful array utilities.
*/
class ArrayUtil
{
/**
* Merges arrays recursively while preserving.
*
* @param array $array1
* @param array $array2
*
* @return array
*
* @see http://php.net/manual/en/function.array-merge-recursive.php#92195
* @see https://github.com/grasmash/bolt/blob/robo-rebase/src/Robo/Common/ArrayManipulator.php#L22
*/
public static function mergeRecursiveDistinct(
array &$array1,
array &$array2
) {
$merged = $array1;
foreach ($array2 as $key => &$value) {
$merged[$key] = self::mergeRecursiveValue($merged, $key, $value);
}
return $merged;
}
/**
* Process the value in an mergeRecursiveDistinct - make a recursive
* call if needed.
*/
protected static function mergeRecursiveValue(&$merged, $key, $value)
{
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
return self::mergeRecursiveDistinct($merged[$key], $value);
}
return $value;
}
/**
* Merges arrays recursively while preserving.
*
* @param array $array1
* @param array $array2
*
* @return array
*
* @see http://php.net/manual/en/function.array-merge-recursive.php#92195
* @see https://github.com/grasmash/bolt/blob/robo-rebase/src/Robo/Common/ArrayManipulator.php#L22
*/
public static function mergeRecursiveSelect(
array &$array1,
array &$array2,
array $selectionList,
$keyPrefix = ''
) {
$merged = $array1;
foreach ($array2 as $key => &$value) {
$merged[$key] = self::mergeRecursiveSelectValue($merged, $key, $value, $selectionList, $keyPrefix);
}
return $merged;
}
/**
* Process the value in an mergeRecursiveDistinct - make a recursive
* call if needed.
*/
protected static function mergeRecursiveSelectValue(&$merged, $key, $value, $selectionList, $keyPrefix)
{
if (is_array($value) && isset($merged[$key]) && is_array($merged[$key])) {
if (self::selectMerge($keyPrefix, $key, $selectionList)) {
return array_merge_recursive($merged[$key], $value);
} else {
return self::mergeRecursiveSelect($merged[$key], $value, $selectionList, "${keyPrefix}${key}.");
}
}
return $value;
}
protected static function selectMerge($keyPrefix, $key, $selectionList)
{
return in_array("${keyPrefix}${key}", $selectionList);
}
/**
* Fills all of the leaf-node values of a nested array with the
* provided replacement value.
*/
public static function fillRecursive(array $data, $fill)
{
$result = [];
foreach ($data as $key => $value) {
$result[$key] = $fill;
if (self::isAssociative($value)) {
$result[$key] = self::fillRecursive($value, $fill);
}
}
return $result;
}
/**
* Return true if the provided parameter is an array, and at least
* one key is non-numeric.
*/
public static function isAssociative($testArray)
{
if (!is_array($testArray)) {
return false;
}
foreach (array_keys($testArray) as $key) {
if (!is_numeric($key)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,51 @@
<?php
namespace Consolidation\Config\Util;
/**
* Fetch a configuration value from a configuration group. If the
* desired configuration value is not found in the most specific
* group named, keep stepping up to the next parent group until a
* value is located.
*
* Given the following constructor inputs:
* - $prefix = "command."
* - $group = "foo.bar.baz"
* - $postfix = ".options."
* Then the `get` method will then consider, in order:
* - command.foo.bar.baz.options
* - command.foo.bar.options
* - command.foo.options
* If any of these contain an option for "$key", then return its value.
*/
class ConfigFallback extends ConfigGroup
{
/**
* @inheritdoc
*/
public function get($key)
{
return $this->getWithFallback($key, $this->group, $this->prefix, $this->postfix);
}
/**
* Fetch an option value from a given key, or, if that specific key does
* not contain a value, then consult various fallback options until a
* value is found.
*
*/
protected function getWithFallback($key, $group, $prefix = '', $postfix = '.')
{
$configKey = "{$prefix}{$group}${postfix}{$key}";
if ($this->config->has($configKey)) {
return $this->config->get($configKey);
}
if ($this->config->hasDefault($configKey)) {
return $this->config->getDefault($configKey);
}
$moreGeneralGroupname = $this->moreGeneralGroupName($group);
if ($moreGeneralGroupname) {
return $this->getWithFallback($key, $moreGeneralGroupname, $prefix, $postfix);
}
return null;
}
}

View File

@@ -0,0 +1,61 @@
<?php
namespace Consolidation\Config\Util;
/**
* Fetch a configuration value from a configuration group. If the
* desired configuration value is not found in the most specific
* group named, keep stepping up to the next parent group until a
* value is located.
*
* Given the following constructor inputs:
* - $prefix = "command."
* - $group = "foo.bar.baz"
* - $postfix = ".options."
* Then the `get` method will then consider, in order:
* - command.foo.bar.baz.options
* - command.foo.bar.options
* - command.foo.options
* If any of these contain an option for "$key", then return its value.
*/
abstract class ConfigGroup
{
protected $config;
protected $group;
protected $prefix;
protected $postfix;
public function __construct($config, $group, $prefix = '', $postfix = '.')
{
$this->config = $config;
$this->group = $group;
$this->prefix = $prefix;
$this->postfix = $postfix;
}
/**
* Return a description of the configuration group (with prefix and postfix).
*/
public function describe($property)
{
return $this->prefix . $this->group . $this->postfix . $property;
}
/**
* Get the requested configuration key from the most specific configuration
* group that contains it.
*/
abstract public function get($key);
/**
* Given a group name, such as "foo.bar.baz", return the next configuration
* group in the fallback hierarchy, e.g. "foo.bar".
*/
protected function moreGeneralGroupName($group)
{
$result = preg_replace('#\.[^.]*$#', '', $group);
if ($result != $group) {
return $result;
}
return false;
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace Consolidation\Config\Util;
use Consolidation\Config\Config;
use Consolidation\Config\ConfigInterface;
/**
* Provides configuration objects with an 'interpolate' method
* that may be used to inject config values into tokens embedded
* in strings.
*/
interface ConfigInterpolatorInterface extends ConfigInterface
{
/**
* interpolate replaces tokens in a string with the correspnding
* value from this config object. Tokens are surrounded by double
* curley braces, e.g. "{{key}}".
*
* Example:
* If the message is 'Hello, {{user.name}}', then the key user.name
* is fetched from the config object, and the token {{user.name}} is
* replaced with the result.
*
* @param string $message Message containing tokens to be replaced
* @param string|bool $default The value to substitute for tokens that
* are not found in the configuration. If `false`, then missing
* tokens are not replaced.
* @return string
*/
public function interpolate($message, $default = '');
/**
* mustInterpolate works exactly like interpolate, save for the fact
* that an exception is thrown is any of the tokens are not replaced.
*/
public function mustInterpolate($message);
}

View File

@@ -0,0 +1,38 @@
<?php
namespace Consolidation\Config\Util;
use Consolidation\Config\Config;
use Consolidation\Config\ConfigInterface;
/**
* Provides configuration objects with an 'interpolate' method
* that may be used to inject config values into tokens embedded
* in strings..
*/
trait ConfigInterpolatorTrait
{
protected $interpolator;
protected function getInterpolator()
{
if (!isset($this->interpolator)) {
$this->interpolator = new Interpolator();
}
return $this->interpolator;
}
/**
* @inheritdoc
*/
public function interpolate($message, $default = '')
{
return $this->getInterpolator()->interpolate($this, $message, $default);
}
/**
* @inheritdoc
*/
public function mustInterpolate($message)
{
return $this->getInterpolator()->mustInterpolate($this, $message);
}
}

View File

@@ -0,0 +1,34 @@
<?php
namespace Consolidation\Config\Util;
/**
* Works like 'getWithFallback', but merges results from all applicable
* groups. Settings from most specific group take precedence.
*/
class ConfigMerge extends ConfigGroup
{
/**
* @inheritdoc
*/
public function get($key)
{
return $this->getWithMerge($key, $this->group, $this->prefix, $this->postfix);
}
/**
* Merge available configuration from each configuration group.
*/
public function getWithMerge($key, $group, $prefix = '', $postfix = '.')
{
$configKey = "{$prefix}{$group}${postfix}{$key}";
$result = $this->config->get($configKey, []);
if (!is_array($result)) {
throw new \UnexpectedValueException($configKey . ' must be a list of settings to apply.');
}
$moreGeneralGroupname = $this->moreGeneralGroupName($group);
if ($moreGeneralGroupname) {
$result += $this->getWithMerge($key, $moreGeneralGroupname, $prefix, $postfix);
}
return $result;
}
}

View File

@@ -0,0 +1,249 @@
<?php
namespace Consolidation\Config\Util;
use Consolidation\Config\Config;
use Consolidation\Config\ConfigInterface;
use Consolidation\Config\Util\ArrayUtil;
use Consolidation\Config\Util\ConfigInterpolatorInterface;
use Consolidation\Config\Util\ConfigInterpolatorTrait;
/**
* 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, ConfigInterpolatorInterface, ConfigRuntimeInterface
{
use ConfigInterpolatorTrait;
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 runtimeConfig()
{
return $this->getContext(self::PROCESS_CONTEXT);
}
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)
{
if (is_array($default)) {
return $this->getUnion($key);
}
return $this->getSingle($key, $default);
}
public function getSingle($key, $default = null)
{
$context = $this->findContext($key);
if ($context) {
return $context->get($key, $default);
}
return $default;
}
public function getUnion($key)
{
$result = [];
foreach (array_reverse($this->contexts) as $name => $config) {
$item = (array) $config->get($key, []);
if ($item !== null) {
$result = array_merge($result, $item);
}
}
return $result;
}
/**
* @inheritdoc
*/
public function set($key, $value)
{
$this->contexts[self::PROCESS_CONTEXT]->set($key, $value);
return $this;
}
/**
* @inheritdoc
*/
public function import($data)
{
$this->unsupported(__FUNCTION__);
}
/**
* @inheritdoc
*/
public function replace($data)
{
$this->unsupported(__FUNCTION__);
}
/**
* @inheritdoc
*/
public function combine($data)
{
$this->unsupported(__FUNCTION__);
}
/**
* @inheritdoc
*/
protected function unsupported($fn)
{
throw new \Exception("The method '$fn' is not supported for the ConfigOverlay class.");
}
/**
* @inheritdoc
*/
public function export()
{
$export = [];
foreach ($this->contexts as $name => $config) {
$exportToMerge = $config->export();
$export = \array_replace_recursive($export, $exportToMerge);
}
return $export;
}
/**
* exportAll returns the export of all contexts, separated into
* separate buckets keyed by context name.
*
* @return array
*/
public function exportAll()
{
$export = [];
foreach ($this->contexts as $name => $config) {
$exportToInsert = $config->export();
$export[$name] = $exportToInsert;
}
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;
}
}

View File

@@ -0,0 +1,21 @@
<?php
namespace Consolidation\Config\Util;
use Consolidation\Config\Config;
use Consolidation\Config\ConfigInterface;
/**
* ConfigRutimeInterface provides a method that returns those elements
* of the configuration that were set at runtime, e.g. via commandline
* options rather than being loaded from a file.
*/
interface ConfigRuntimeInterface
{
/**
* runtimeConfig returns those elements of the configuration not
* loaded from a file.
*
* @return ConfigInterface
*/
public function runtimeConfig();
}

View File

@@ -0,0 +1,96 @@
<?php
namespace Consolidation\Config\Util;
use Consolidation\Config\Config;
use Consolidation\Config\ConfigInterface;
/**
* Provide a configuration object that fetches values from environment
* variables.
*/
class EnvConfig implements ConfigInterface
{
/** @var string */
protected $prefix;
/**
* EnvConfig constructor
*
* @param $prefix The string to appear before every environment
* variable key. For example, if the prefix is 'MYAPP_', then
* the key 'foo.bar' will be fetched from the environment variable
* MYAPP_FOO_BAR.
*/
public function __construct($prefix)
{
// Ensure that the prefix is always uppercase, and always
// ends with a '_', regardless of the form the caller provided.
$this->prefix = strtoupper(rtrim($prefix, '_')) . '_';
}
/**
* @inheritdoc
*/
public function has($key)
{
return $this->get($key) !== null;
}
/**
* @inheritdoc
*/
public function get($key, $defaultFallback = null)
{
$envKey = $this->prefix . strtoupper(strtr($key, '.-', '__'));
$envKey = str_replace($this->prefix . $this->prefix, $this->prefix, $envKey);
return getenv($envKey) ?: $defaultFallback;
}
/**
* @inheritdoc
*/
public function set($key, $value)
{
throw new \Exception('Cannot call "set" on environmental configuration.');
}
/**
* @inheritdoc
*/
public function import($data)
{
// no-op
}
/**
* @inheritdoc
*/
public function export()
{
return [];
}
/**
* @inheritdoc
*/
public function hasDefault($key)
{
return false;
}
/**
* @inheritdoc
*/
public function getDefault($key, $defaultFallback = null)
{
return $defaultFallback;
}
/**
* @inheritdoc
*/
public function setDefault($key, $value)
{
throw new \Exception('Cannot call "setDefault" on environmental configuration.');
}
}

View File

@@ -0,0 +1,97 @@
<?php
namespace Consolidation\Config\Util;
use Consolidation\Config\Config;
use Consolidation\Config\ConfigInterface;
/**
* Provides configuration objects with an 'interpolate' method
* that may be used to inject config values into tokens embedded
* in strings..
*/
class Interpolator
{
/**
* interpolate replaces tokens in a string with the correspnding
* value from this config object. Tokens are surrounded by double
* curley braces, e.g. "{{key}}".
*
* Example:
* If the message is 'Hello, {{user.name}}', then the key user.name
* is fetched from the config object, and the token {{user.name}} is
* replaced with the result.
*
* @param string $message Message containing tokens to be replaced
* @param string|bool $default The value to substitute for tokens that
* are not found in the configuration. If `false`, then missing
* tokens are not replaced.
* @return string
*/
public function interpolate($data, $message, $default = '')
{
$replacements = $this->replacements($data, $message, $default);
return strtr($message, $replacements);
}
/**
* @inheritdoc
*/
public function mustInterpolate($data, $message)
{
$result = $this->interpolate($data, $message, false);
$tokens = $this->findTokens($result);
if (!empty($tokens)) {
throw new \Exception('The following required keys were not found in configuration: ' . implode(',', $tokens));
}
return $result;
}
/**
* findTokens finds all of the tokens in the provided message
*
* @param string $message String with tokens
* @return string[] map of token to key, e.g. {{key}} => key
*/
public function findTokens($message)
{
if (!preg_match_all('#{{([a-zA-Z0-9._-]+)}}#', $message, $matches, PREG_SET_ORDER)) {
return [];
}
$tokens = [];
foreach ($matches as $matchSet) {
list($sourceText, $key) = $matchSet;
$tokens[$sourceText] = $key;
}
return $tokens;
}
/**
* Replacements looks up all of the replacements in the configuration
* object, given the token keys from the provided message. Keys that
* do not exist in the configuration are replaced with the default value.
*/
public function replacements($data, $message, $default = '')
{
$tokens = $this->findTokens($message);
$replacements = [];
foreach ($tokens as $sourceText => $key) {
$replacementText = $this->get($data, $key, $default);
if ($replacementText !== false) {
$replacements[$sourceText] = $replacementText;
}
}
return $replacements;
}
protected function get($data, $key, $default)
{
if (is_array($data)) {
return array_key_exists($key, $data) ? $data[$key] : $default;
}
if ($data instanceof ConfigInterface) {
return $data->get($key, $default);
}
throw new \Exception('Bad data type provided to Interpolator');
}
}