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: /**
24: * Merge the list of $properties with all parent classes of the current class.
25: *
26: * ### Options:
27: *
28: * - `associative` - A list of properties that should be treated as associative arrays.
29: * Properties in this list will be passed through Hash::normalize() before merging.
30: *
31: * @param array $properties An array of properties and the merge strategy for them.
32: * @param array $options The options to use when merging properties.
33: * @return void
34: */
35: protected function _mergeVars($properties, $options = [])
36: {
37: $class = get_class($this);
38: $parents = [];
39: while (true) {
40: $parent = get_parent_class($class);
41: if (!$parent) {
42: break;
43: }
44: $parents[] = $parent;
45: $class = $parent;
46: }
47: foreach ($properties as $property) {
48: if (!property_exists($this, $property)) {
49: continue;
50: }
51: $thisValue = $this->{$property};
52: if ($thisValue === null || $thisValue === false) {
53: continue;
54: }
55: $this->_mergeProperty($property, $parents, $options);
56: }
57: }
58:
59: /**
60: * Merge a single property with the values declared in all parent classes.
61: *
62: * @param string $property The name of the property being merged.
63: * @param array $parentClasses An array of classes you want to merge with.
64: * @param array $options Options for merging the property, see _mergeVars()
65: * @return void
66: */
67: protected function _mergeProperty($property, $parentClasses, $options)
68: {
69: $thisValue = $this->{$property};
70: $isAssoc = false;
71: if (isset($options['associative']) &&
72: in_array($property, (array)$options['associative'])
73: ) {
74: $isAssoc = true;
75: }
76:
77: if ($isAssoc) {
78: $thisValue = Hash::normalize($thisValue);
79: }
80: foreach ($parentClasses as $class) {
81: $parentProperties = get_class_vars($class);
82: if (empty($parentProperties[$property])) {
83: continue;
84: }
85: $parentProperty = $parentProperties[$property];
86: if (!is_array($parentProperty)) {
87: continue;
88: }
89: $thisValue = $this->_mergePropertyData($thisValue, $parentProperty, $isAssoc);
90: }
91: $this->{$property} = $thisValue;
92: }
93:
94: /**
95: * Merge each of the keys in a property together.
96: *
97: * @param array $current The current merged value.
98: * @param array $parent The parent class' value.
99: * @param bool $isAssoc Whether or not the merging should be done in associative mode.
100: * @return mixed The updated value.
101: */
102: protected function _mergePropertyData($current, $parent, $isAssoc)
103: {
104: if (!$isAssoc) {
105: return array_merge($parent, $current);
106: }
107: $parent = Hash::normalize($parent);
108: foreach ($parent as $key => $value) {
109: if (!isset($current[$key])) {
110: $current[$key] = $value;
111: }
112: }
113:
114: return $current;
115: }
116: }
117: