Browse Source

Add merge option to InstanceConfig trait

This addresses the problem raised in b81ebffe
AD7six 12 years ago
parent
commit
08a6faf077

+ 1 - 1
src/Controller/Component.php

@@ -105,7 +105,7 @@ class Component extends Object implements EventListener {
 	public function __construct(ComponentRegistry $registry, $config = []) {
 		$this->_registry = $registry;
 
-		$this->config($config);
+		$this->config($config, null, true);
 
 		$this->_set($this->config());
 

+ 1 - 1
src/Controller/Component/Auth/AbstractPasswordHasher.php

@@ -41,7 +41,7 @@ abstract class AbstractPasswordHasher {
  * @param array $config Array of config.
  */
 	public function __construct($config = array()) {
-		$this->config($config);
+		$this->config($config, null, true);
 	}
 
 /**

+ 1 - 1
src/Controller/Component/Auth/BaseAuthenticate.php

@@ -79,7 +79,7 @@ abstract class BaseAuthenticate {
  */
 	public function __construct(ComponentRegistry $registry, $config) {
 		$this->_registry = $registry;
-		$this->config($config);
+		$this->config($config, null, true);
 	}
 
 /**

+ 1 - 1
src/Controller/Component/Auth/BaseAuthorize.php

@@ -81,7 +81,7 @@ abstract class BaseAuthorize {
 		$this->_registry = $registry;
 		$controller = $registry->getController();
 		$this->controller($controller);
-		$this->config($config);
+		$this->config($config, null, true);
 	}
 
 /**

+ 22 - 9
src/Core/InstanceConfigTrait.php

@@ -15,6 +15,7 @@
 namespace Cake\Core;
 
 use Cake\Error;
+use Cake\Utility\Hash;
 
 /**
  * A trait for reading and writing instance config
@@ -67,16 +68,17 @@ trait InstanceConfigTrait {
  * @param string|array|null $key the key to get/set, or a complete array of configs
  * @param mixed|null $value the value to set
  * @return mixed|null null for write, or whatever is in the config on read
+ * @param bool $merge whether to merge or overwrite existing config
  * @throws \Cake\Error\Exception When trying to set a key that is invalid
  */
-	public function config($key = null, $value = null) {
+	public function config($key = null, $value = null, $merge = false) {
 		if (!$this->_configInitialized) {
 			$this->_config = $this->_defaultConfig;
 			$this->_configInitialized = true;
 		}
 
-		if (is_array($key) || func_num_args() === 2) {
-			return $this->_configWrite($key, $value);
+		if (is_array($key) || func_num_args() >= 2) {
+			return $this->_configWrite($key, $value, $merge);
 		}
 
 		return $this->_configRead($key);
@@ -117,10 +119,25 @@ trait InstanceConfigTrait {
  *
  * @throws Cake\Error\Exception if attempting to clobber existing config
  * @param string|array $key
- * @param mixed|null $value
+ * @param mixed $value
+ * @param bool $merge
  * @return void
  */
-	protected function _configWrite($key, $value = null) {
+	protected function _configWrite($key, $value, $merge = null) {
+		if (is_string($key) && $value === null) {
+			return $this->_configDelete($key);
+		}
+
+		if ($merge) {
+			if (is_array($key)) {
+				$update = $key;
+			} else {
+				$update = [$key => $value];
+			}
+			$this->_config = Hash::merge($this->_config, Hash::expand($update));
+			return;
+		}
+
 		if (is_array($key)) {
 			foreach ($key as $k => $val) {
 				$this->_configWrite($k, $val);
@@ -128,10 +145,6 @@ trait InstanceConfigTrait {
 			return;
 		}
 
-		if ($value === null) {
-			return $this->_configDelete($key);
-		}
-
 		if (strpos($key, '.') === false) {
 			$this->_config[$key] = $value;
 			return;

+ 1 - 1
src/ORM/Behavior.php

@@ -126,7 +126,7 @@ class Behavior implements EventListener {
  * @param array $config The config for this behavior.
  */
 	public function __construct(Table $table, array $config = []) {
-		$this->config($config);
+		$this->config($config, null, true);
 	}
 
 /**

+ 1 - 1
src/Routing/DispatcherFilter.php

@@ -53,7 +53,7 @@ abstract class DispatcherFilter implements EventListener {
  * @param array $config Settings for the filter.
  */
 	public function __construct($config = []) {
-		$this->config($config);
+		$this->config($config, null, true);
 	}
 
 /**

+ 1 - 1
src/View/Helper.php

@@ -153,7 +153,7 @@ class Helper extends Object implements EventListener {
 		if ($config) {
 			$config = Hash::merge($this->_defaultConfig, $config);
 		}
-		$this->config($config);
+		$this->config($config, null, true);
 
 		if (!empty($this->helpers)) {
 			$this->_helperMap = $View->Helpers->normalizeArray($this->helpers);

+ 1 - 1
src/View/StringTemplate.php

@@ -60,7 +60,7 @@ class StringTemplate {
  * @param array $templates A set of templates to add.
  */
 	public function __construct(array $config = null) {
-		$this->config($config);
+		$this->config($config, null, true);
 	}
 
 /**

+ 126 - 2
tests/TestCase/Core/InstanceConfigTraitTest.php

@@ -260,7 +260,131 @@ class InstanceConfigTraitTest extends TestCase {
  * @return void
  */
 	public function testSetClobber() {
-		$this->object->config(['a.nested.value' => 'not possible']);
+		$this->object->config(['a.nested.value' => 'not possible'], null, false);
+		$result = $this->object->config();
+	}
+
+/**
+ * testMerge
+ *
+ * @return void
+ */
+	public function testMerge() {
+		$this->object->config(['a' => ['nother' => 'value']], null, true);
+
+		$this->assertSame(
+			[
+				'some' => 'string',
+				'a' => [
+					'nested' => 'value',
+					'nother' => 'value'
+				]
+			],
+			$this->object->config(),
+			'Merging should not delete untouched array values'
+		);
+	}
+
+/**
+ * testMergeDotKey
+ *
+ * @return void
+ */
+	public function testMergeDotKey() {
+		$this->object->config('a.nother', 'value', true);
+
+		$this->assertSame(
+			[
+				'some' => 'string',
+				'a' => [
+					'nested' => 'value',
+					'nother' => 'value'
+				]
+			],
+			$this->object->config(),
+			'Should act the same as having passed the equivalent array to the config function'
+		);
+
+		$this->object->config(['a.nextra' => 'value'], null, true);
+
+		$this->assertSame(
+			[
+				'some' => 'string',
+				'a' => [
+					'nested' => 'value',
+					'nother' => 'value',
+					'nextra' => 'value'
+				]
+			],
+			$this->object->config(),
+			'Merging should not delete untouched array values'
+		);
+	}
+
+/**
+ * testSetDefaultsMerge
+ *
+ * @return void
+ */
+	public function testSetDefaultsMerge() {
+		$this->object->config(['a' => ['nother' => 'value']], null, true);
+
+		$this->assertSame(
+			[
+				'some' => 'string',
+				'a' => [
+					'nested' => 'value',
+					'nother' => 'value'
+				]
+			],
+			$this->object->config(),
+			'First access should act like any subsequent access'
+		);
+	}
+
+/**
+ * testSetDefaultsNoMerge
+ *
+ * @return void
+ */
+	public function testSetDefaultsNoMerge() {
+		$this->object->config(['a' => ['nother' => 'value']], null, false);
+
+		$this->assertSame(
+			[
+				'some' => 'string',
+				'a' => [
+					'nother' => 'value'
+				]
+			],
+			$this->object->config(),
+			'If explicitly no-merge, array values should be overwritten'
+		);
+	}
+
+/**
+ * testSetMergeNoClobber
+ *
+ * Merging offers no such protection of clobbering a value whilst implemented
+ * using the Hash class
+ *
+ * @return void
+ */
+	public function testSetMergeNoClobber() {
+		$this->object->config(['a.nested.value' => 'it is possible'], null, true);
+
+		$this->assertSame(
+			[
+				'some' => 'string',
+				'a' => [
+					'nested' => [
+						'value' => 'it is possible'
+					]
+				]
+			],
+			$this->object->config(),
+			'When merging a scalar property will be overwritten with an array'
+		);
 	}
 
 /**
@@ -340,7 +464,7 @@ class InstanceConfigTraitTest extends TestCase {
 	}
 
 /**
- * testSetClobber
+ * testDeleteClobber
  *
  * @expectedException \Exception
  * @expectedExceptionMessage Cannot unset a.nested.value.whoops