<?php
namespace phpDocumentor\Configuration;
use Doctrine\Common\Annotations\AnnotationReader;
class Merger
{
private $reader;
public function __construct(AnnotationReader $reader)
{
$this->reader = $reader;
}
public function run($destination, $source, $default = null)
{
$result = null;
if (is_object($destination) && is_object($source) && get_class($destination) == get_class($source)) {
$result = $this->mergeObject($destination, $source);
} elseif (is_array($source) && is_array($destination)) {
$result = $this->mergeArray($destination, $source);
} elseif (!is_null($source) && $source !== $default) {
$result = $source;
}
return $result;
}
private function mergeObject($destinationObject, $sourceObject)
{
$reflectedDestination = new \ReflectionObject($destinationObject);
$reflectedSource = new \ReflectionObject($sourceObject);
$defaultPropertyValues = $reflectedDestination->getDefaultProperties();
foreach ($reflectedSource->getProperties() as $sourceProperty) {
$destinationObject = $this->mergeProperty(
$destinationObject,
$reflectedDestination->getProperty($sourceProperty->getName()),
$sourceObject,
$sourceProperty,
$defaultPropertyValues
);
}
return $destinationObject;
}
private function mergeArray($destinationArray, $sourceArray)
{
$result = array();
foreach ($destinationArray as $key => $destinationArrayItem) {
if (is_int($key)) {
$result[] = $destinationArrayItem;
} else {
$result[$key] = $destinationArrayItem;
}
}
foreach ($sourceArray as $key => $sourceArrayItem) {
if (is_int($key)) {
$result[] = $sourceArrayItem;
} else {
$result[$key] = $this->run($result[$key], $sourceArrayItem);
}
}
return $result;
}
private function mergeProperty(
$destinationObject,
\ReflectionProperty $destinationProperty,
$sourceObject,
\ReflectionProperty $sourceProperty,
array $defaultPropertyValues
) {
$sourceProperty->setAccessible(true);
$destinationProperty->setAccessible(true);
$destinationValue = $destinationProperty->getValue($destinationObject);
$sourceValue = $sourceProperty->getValue($sourceObject);
$sourcePropertyDefaultValue = isset($defaultPropertyValues[$sourceProperty->getName()])
? $defaultPropertyValues[$sourceProperty->getName()]
: null;
if ($this->shouldPropertyBeReplaced($destinationProperty)) {
$destinationValue = null;
}
$result = $this->run($destinationValue, $sourceValue, $sourcePropertyDefaultValue);
if ($result !== null) {
$destinationProperty->setValue($destinationObject, $result);
}
$destinationProperty->setAccessible($destinationProperty->isPublic());
$sourceProperty->setAccessible($sourceProperty->isPublic());
return $destinationObject;
}
private function shouldPropertyBeReplaced(\ReflectionProperty $destinationProperty)
{
return (bool) $this->reader->getPropertyAnnotation(
$destinationProperty,
'phpDocumentor\Configuration\Merger\Annotation\Replace'
);
}
}