wiki.techinc.nl/includes/Settings/Config/MergeStrategy.php
daniel f9b589f556 config-schema: Define types for all arrays.
This patch ensures that we know which arrays are lists (JsonSchema type
"array") and which are maps (JsonSchema type "object"). We can then
default to array_merge for lists and to array_plus for maps. This seems
clearer than requiring an explicit merge strategy to be declared for all
arrays.

This patch specified a mergeTrategy for some config variables that need
behavior different from the default.

This patch also changes the merging behavior to allow non-array values
to replace arrays and vice versa. It also changes the behavior of
defaults to allow falsy values to override non-falsy defaults.

Bug: T300129
Change-Id: Ia7b0c0250af6a957eac1efb554fb47511f5e093f
2022-02-23 14:09:41 +01:00

116 lines
2.8 KiB
PHP

<?php
namespace MediaWiki\Settings\Config;
use MediaWiki\Settings\SettingsBuilderException;
class MergeStrategy {
/** @var string */
public const ARRAY_MERGE_RECURSIVE = 'array_merge_recursive';
/** @var string */
public const ARRAY_REPLACE_RECURSIVE = 'array_replace_recursive';
/** @var string */
public const ARRAY_PLUS_2D = 'array_plus_2d';
/** @var string */
public const ARRAY_PLUS = 'array_plus';
/** @var string */
public const ARRAY_MERGE = 'array_merge';
/** @var string */
public const REPLACE = 'replace';
/** @var string */
private $name;
/** @var bool */
private $reversed;
/** @var MergeStrategy[] */
private static $strategies = [];
/** @var MergeStrategy[] */
private static $reversedStrategies = [];
/**
* @param string $name
* @return static
*/
public static function newFromName( string $name ): self {
if ( !array_key_exists( $name, self::$strategies ) ) {
self::$strategies[$name] = new MergeStrategy( $name );
}
return self::$strategies[$name];
}
/**
* @param string $name
* @param bool $reversed
*/
private function __construct( string $name, bool $reversed = false ) {
$this->name = $name;
$this->reversed = $reversed;
}
/**
* @return string
*/
public function getName(): string {
return $this->name;
}
/**
* Merge $source into $destination.
*
* @note For all merge strategies except self::ARRAY_MERGE_RECURSIVE,
* for the values that have the same key, the value from $source will
* override the value in the $destination.
*
* @param array $destination
* @param array $source
* @return array
*/
public function merge( array $destination, array $source ): array {
if ( $this->reversed ) {
[ $destination, $source ] = [ $source, $destination ];
}
switch ( $this->name ) {
case self::REPLACE:
return $source;
case self::ARRAY_MERGE_RECURSIVE:
return array_merge_recursive( $destination, $source );
case self::ARRAY_REPLACE_RECURSIVE:
return array_replace_recursive( $destination, $source );
case self::ARRAY_PLUS_2D:
return wfArrayPlus2d( $source, $destination );
case self::ARRAY_PLUS:
return $source + $destination;
case self::ARRAY_MERGE:
return array_merge( $destination, $source );
default:
throw new SettingsBuilderException(
'Unknown merge strategy {name}',
[ 'name' => $this->name ]
);
}
}
/**
* Create a reversed merge strategy, which will merge $destination into $source
* instead of $source into $destination.
*
* @see self::merge
* @return MergeStrategy
*/
public function reverse(): self {
if ( !array_key_exists( $this->name, self::$reversedStrategies ) ) {
self::$reversedStrategies[$this->name] = new self( $this->name, !$this->reversed );
}
return self::$reversedStrategies[$this->name];
}
}