private function mergeProperty(
        $destinationObject,
        \ReflectionProperty $destinationProperty,
        $sourceObject,
        \ReflectionProperty $sourceProperty,
        array $defaultPropertyValues
    ) {
        // Allow the source and destination properties to be readable
        $sourceProperty->setAccessible(true);
        $destinationProperty->setAccessible(true);

        // Retrieve the current value for both the destination and source
        $destinationValue = $destinationProperty->getValue($destinationObject);
        $sourceValue = $sourceProperty->getValue($sourceObject);

        // Find out what the default value for this property is
        $sourcePropertyDefaultValue = isset($defaultPropertyValues[$sourceProperty->getName()])
            ? $defaultPropertyValues[$sourceProperty->getName()]
            : null;

        // if a property is annotated with the 'Replace' annotation then we null the destination location,
        // causing the value from source to be copied as-is to the destination object instead of merging it.
        // but only if the value that is to-be-copied is actually copied
        if ($this->shouldPropertyBeReplaced($destinationProperty)) {
            $destinationValue = null;
        }

        // Merge the values of the two properties!
        $result = $this->run($destinationValue, $sourceValue, $sourcePropertyDefaultValue);

        // No result? No save. We only update the destination if the resulting merge is a value
        if ($result !== null) {
            $destinationProperty->setValue($destinationObject, $result);
        }

        // Make protected and private properties inaccessible again
        $destinationProperty->setAccessible($destinationProperty->isPublic());
        $sourceProperty->setAccessible($sourceProperty->isPublic());

        return $destinationObject;
    }