119 lines
3.9 KiB
PHP
119 lines
3.9 KiB
PHP
|
<?php
|
||
|
declare(strict_types=1);
|
||
|
|
||
|
/**
|
||
|
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
|
||
|
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||
|
*
|
||
|
* Licensed under The MIT License
|
||
|
* Redistributions of files must retain the above copyright notice.
|
||
|
*
|
||
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
||
|
* @link https://cakephp.org CakePHP(tm) Project
|
||
|
* @since 3.0.0
|
||
|
* @license https://opensource.org/licenses/mit-license.php MIT License
|
||
|
*/
|
||
|
namespace Cake\Utility;
|
||
|
|
||
|
/**
|
||
|
* Provides features for merging object properties recursively with
|
||
|
* parent classes.
|
||
|
*/
|
||
|
trait MergeVariablesTrait
|
||
|
{
|
||
|
/**
|
||
|
* Merge the list of $properties with all parent classes of the current class.
|
||
|
*
|
||
|
* ### Options:
|
||
|
*
|
||
|
* - `associative` - A list of properties that should be treated as associative arrays.
|
||
|
* Properties in this list will be passed through Hash::normalize() before merging.
|
||
|
*
|
||
|
* @param array<string> $properties An array of properties and the merge strategy for them.
|
||
|
* @param array<string, mixed> $options The options to use when merging properties.
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function _mergeVars(array $properties, array $options = []): void
|
||
|
{
|
||
|
$class = static::class;
|
||
|
$parents = [];
|
||
|
while (true) {
|
||
|
$parent = get_parent_class($class);
|
||
|
if (!$parent) {
|
||
|
break;
|
||
|
}
|
||
|
$parents[] = $parent;
|
||
|
$class = $parent;
|
||
|
}
|
||
|
foreach ($properties as $property) {
|
||
|
if (!property_exists($this, $property)) {
|
||
|
continue;
|
||
|
}
|
||
|
$thisValue = $this->{$property};
|
||
|
if ($thisValue === null || $thisValue === false) {
|
||
|
continue;
|
||
|
}
|
||
|
$this->_mergeProperty($property, $parents, $options);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Merge a single property with the values declared in all parent classes.
|
||
|
*
|
||
|
* @param string $property The name of the property being merged.
|
||
|
* @param array<string> $parentClasses An array of classes you want to merge with.
|
||
|
* @param array<string, mixed> $options Options for merging the property, see _mergeVars()
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function _mergeProperty(string $property, array $parentClasses, array $options): void
|
||
|
{
|
||
|
$thisValue = $this->{$property};
|
||
|
$isAssoc = false;
|
||
|
if (
|
||
|
isset($options['associative']) &&
|
||
|
in_array($property, (array)$options['associative'], true)
|
||
|
) {
|
||
|
$isAssoc = true;
|
||
|
}
|
||
|
|
||
|
if ($isAssoc) {
|
||
|
$thisValue = Hash::normalize($thisValue);
|
||
|
}
|
||
|
foreach ($parentClasses as $class) {
|
||
|
$parentProperties = get_class_vars($class);
|
||
|
if (empty($parentProperties[$property])) {
|
||
|
continue;
|
||
|
}
|
||
|
$parentProperty = $parentProperties[$property];
|
||
|
if (!is_array($parentProperty)) {
|
||
|
continue;
|
||
|
}
|
||
|
$thisValue = $this->_mergePropertyData($thisValue, $parentProperty, $isAssoc);
|
||
|
}
|
||
|
$this->{$property} = $thisValue;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Merge each of the keys in a property together.
|
||
|
*
|
||
|
* @param array $current The current merged value.
|
||
|
* @param array $parent The parent class' value.
|
||
|
* @param bool $isAssoc Whether the merging should be done in associative mode.
|
||
|
* @return array The updated value.
|
||
|
*/
|
||
|
protected function _mergePropertyData(array $current, array $parent, bool $isAssoc)
|
||
|
{
|
||
|
if (!$isAssoc) {
|
||
|
return array_merge($parent, $current);
|
||
|
}
|
||
|
$parent = Hash::normalize($parent);
|
||
|
foreach ($parent as $key => $value) {
|
||
|
if (!isset($current[$key])) {
|
||
|
$current[$key] = $value;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $current;
|
||
|
}
|
||
|
}
|