1: <?php
2: /**
3: * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
4: * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
5: *
6: * Licensed under The MIT License
7: * Redistributions of files must retain the above copyright notice.
8: *
9: * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
10: * @link https://cakephp.org CakePHP(tm) Project
11: * @since 3.0.0
12: * @license https://opensource.org/licenses/mit-license.php MIT License
13: */
14: namespace Cake\Utility;
15:
16: /**
17: * Provides features for merging object properties recursively with
18: * parent classes.
19: */
20: trait MergeVariablesTrait
21: {
22: /**
23: * Merge the list of $properties with all parent classes of the current class.
24: *
25: * ### Options:
26: *
27: * - `associative` - A list of properties that should be treated as associative arrays.
28: * Properties in this list will be passed through Hash::normalize() before merging.
29: *
30: * @param string[] $properties An array of properties and the merge strategy for them.
31: * @param array $options The options to use when merging properties.
32: * @return void
33: */
34: protected function _mergeVars($properties, $options = [])
35: {
36: $class = get_class($this);
37: $parents = [];
38: while (true) {
39: $parent = get_parent_class($class);
40: if (!$parent) {
41: break;
42: }
43: $parents[] = $parent;
44: $class = $parent;
45: }
46: foreach ($properties as $property) {
47: if (!property_exists($this, $property)) {
48: continue;
49: }
50: $thisValue = $this->{$property};
51: if ($thisValue === null || $thisValue === false) {
52: continue;
53: }
54: $this->_mergeProperty($property, $parents, $options);
55: }
56: }
57:
58: /**
59: * Merge a single property with the values declared in all parent classes.
60: *
61: * @param string $property The name of the property being merged.
62: * @param array $parentClasses An array of classes you want to merge with.
63: * @param array $options Options for merging the property, see _mergeVars()
64: * @return void
65: */
66: protected function _mergeProperty($property, $parentClasses, $options)
67: {
68: $thisValue = $this->{$property};
69: $isAssoc = false;
70: if (isset($options['associative']) &&
71: in_array($property, (array)$options['associative'])
72: ) {
73: $isAssoc = true;
74: }
75:
76: if ($isAssoc) {
77: $thisValue = Hash::normalize($thisValue);
78: }
79: foreach ($parentClasses as $class) {
80: $parentProperties = get_class_vars($class);
81: if (empty($parentProperties[$property])) {
82: continue;
83: }
84: $parentProperty = $parentProperties[$property];
85: if (!is_array($parentProperty)) {
86: continue;
87: }
88: $thisValue = $this->_mergePropertyData($thisValue, $parentProperty, $isAssoc);
89: }
90: $this->{$property} = $thisValue;
91: }
92:
93: /**
94: * Merge each of the keys in a property together.
95: *
96: * @param array $current The current merged value.
97: * @param array $parent The parent class' value.
98: * @param bool $isAssoc Whether or not the merging should be done in associative mode.
99: * @return mixed The updated value.
100: */
101: protected function _mergePropertyData($current, $parent, $isAssoc)
102: {
103: if (!$isAssoc) {
104: return array_merge($parent, $current);
105: }
106: $parent = Hash::normalize($parent);
107: foreach ($parent as $key => $value) {
108: if (!isset($current[$key])) {
109: $current[$key] = $value;
110: }
111: }
112:
113: return $current;
114: }
115: }
116: