-
-
Save zdenekdrahos/b65cc64ec7e2801ce92e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Diff\Patcher; | |
use Diff\Comparer\ValueComparer; | |
class MapAccumulator | |
{ | |
private $base; | |
private $currentKey; | |
private $comparer; | |
private $errorHandler; | |
public function __construct(array $base, ValueComparer $c, callable $errorHandler) | |
{ | |
$this->base = $base; | |
$this->comparer = $c; | |
$this->errorHandler = $errorHandler; | |
} | |
public function setCurrentKey($key) | |
{ | |
$this->currentKey = $key; | |
} | |
public function addError($message) | |
{ | |
$this->errorHandler->__invoke($message); | |
} | |
public function existsDiffOperation() | |
{ | |
return array_key_exists($this->currentKey, $this->base); | |
} | |
public function isOperationChanged($diffOp) | |
{ | |
return !$this->comparer->valuesAreEqual($this->base[$this->currentKey], $diffOp->getOldValue()); | |
} | |
public function replaceOldOperation($diffOp) | |
{ | |
$newValue = $diffOp->getNewValue(); | |
$this->setOperation($newValue); | |
} | |
public function getCurrentOperation() | |
{ | |
return $this->base[$this->currentKey]; | |
} | |
public function setOperation($value) | |
{ | |
$this->base[$this->currentKey] = $value; | |
} | |
public function removeCurrentOperation() | |
{ | |
unset($this->base[$this->currentKey]); | |
} | |
public function getResult() | |
{ | |
return $this->base; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Diff\Patcher; | |
use Diff\DiffOp\Diff\Diff; | |
use Diff\Patcher\ListPatcher; | |
class MapOrListPatcher implements Patcher | |
{ | |
private $listPatcher; | |
private $mapPatcher; | |
public function __construct(ListPatcher $l, Patcher $m) | |
{ | |
$this->listPatcher = $l; | |
$this->mapPatcher = $m; | |
} | |
public function patch(array $base, Diff $diff) | |
{ | |
if ($diff->looksAssociative()) { | |
return $this->mapPatcher->patch($base, $diff); | |
} else { | |
return $this->listPatcher->patch($base, $diff); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Diff\Patcher; | |
use Diff\Comparer\StrictComparer; | |
use Diff\Comparer\ValueComparer; | |
use Diff\DiffOp\Diff\Diff; | |
/** | |
* Map patcher. | |
* | |
* @since 0.4 | |
* | |
* @licence GNU GPL v2+ | |
* @author Jeroen De Dauw < jeroendedauw@gmail.com > | |
*/ | |
class MapPatcher extends ThrowingPatcher { | |
/** | |
* @var Patcher | |
*/ | |
private $mapListPatcher; | |
/** | |
* @var ValueComparer | |
*/ | |
private $comparer; | |
/** | |
* @since 0.4 | |
* | |
* @param bool $throwErrors | |
* @param Patcher|null $listPatcher The patcher that will be used for lists in the value | |
*/ | |
public function __construct( $throwErrors = false, Patcher $listPatcher = null ) { | |
parent::__construct( $throwErrors ); | |
if ( $listPatcher === null ) { | |
$listPatcher = new ListPatcher( $throwErrors ); | |
} | |
$this->mapListPatcher = new MapOrListPatcher($listPatcher, $this); | |
$this->comparer = new StrictComparer(); | |
} | |
/** | |
* @see Patcher::patch | |
* | |
* Applies the provided diff to the provided array and returns the result. | |
* The array is treated as a map, ie keys are held into account. | |
* | |
* It is possible to pass in non-associative diffs (those for which isAssociative) | |
* returns false, however the likely intended behavior can be obtained via | |
* a list patcher. | |
* | |
* @since 0.4 | |
* | |
* @param array $base | |
* @param Diff $diff | |
* | |
* @return array | |
* @throws PatcherException | |
*/ | |
public function patch( array $base, Diff $diff ) { | |
$errorHandler = function ($message) { | |
$this->handleError($message); | |
}; | |
$accumulator = new MapAccumulator($base, $this->comparer, $errorHandler); | |
$strategies = new OperationStrategies($accumulator, $this->mapListPatcher); | |
static $factory = array( | |
'Diff\DiffOp\DiffOpAdd' => 'add', | |
'Diff\DiffOp\Diff\Diff' => 'diff', | |
'Diff\DiffOp\DiffOpRemove' => 'remove', | |
'Diff\DiffOp\DiffOpChange' => 'change', | |
); | |
foreach ($diff as $key => $diffOp) { | |
$type = get_class($diffOp); | |
$method = array_key_exists($type, $factory) ? $factory[$type] : 'unknownOperation'; | |
$accumulator->setCurrentKey($key); | |
$strategies->$method($diffOp); | |
} | |
return $accumulator->getResult(); | |
} | |
/** | |
* Sets the value comparer that should be used to determine if values are equal. | |
* | |
* @since 0.6 | |
* | |
* @param ValueComparer $comparer | |
*/ | |
public function setValueComparer( ValueComparer $comparer ) { | |
$this->comparer = $comparer; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Diff\Patcher; | |
use Diff\DiffOp\DiffOpAdd; | |
use Diff\DiffOp\Diff\Diff; | |
use Diff\DiffOp\DiffOpRemove; | |
use Diff\DiffOp\DiffOpChange; | |
class OperationStrategies | |
{ | |
private $accumulator; | |
private $mapListPatcher; | |
public function __construct(MapAccumulator $a, Patcher $p) | |
{ | |
$this->accumulator = $a; | |
$this->mapListPatcher = $p; | |
} | |
public function add(DiffOpAdd $diffOp) | |
{ | |
if ($this->accumulator->existsDiffOperation()) { | |
$this->accumulator->addError('Cannot add an element already present in a map'); | |
return; | |
} | |
$this->accumulator->replaceOldOperation($diffOp); | |
} | |
public function diff(Diff $diffOp) | |
{ | |
if (!$this->accumulator->existsDiffOperation() | |
&& ($diffOp->getChanges() !== array() || $diffOp->getRemovals() !== array()) | |
) { | |
$this->accumulator->addError('Cannot apply a diff with non-add operations to an element not present in a map'); | |
return; | |
} | |
if (!$this->accumulator->existsDiffOperation()) { | |
$this->accumulator->setOperation(array()); | |
} | |
$diff = $this->mapListPatcher->patch($this->accumulator->getCurrentOperation(), $diffOp); | |
$this->accumulator->setOperation($diff); | |
} | |
public function remove(DiffOpRemove $diffOp) | |
{ | |
if (!$this->accumulator->existsDiffOperation()) { | |
$this->accumulator->addError('Cannot do a non-add operation with an element not present in a map'); | |
return; | |
} | |
if ($this->accumulator->isOperationChanged($diffOp)) { | |
$this->accumulator->addError('Tried removing a map value that mismatches the current value'); | |
return; | |
} | |
$this->accumulator->removeCurrentOperation(); | |
} | |
public function change(DiffOpChange $diffOp) | |
{ | |
if (!$this->accumulator->existsDiffOperation()) { | |
$this->accumulator->addError('Cannot do a non-add operation with an element not present in a map'); | |
return; | |
} | |
if ($this->accumulator->isOperationChanged($diffOp)) { | |
$this->accumulator->addError('Tried changing a map value from an invalid source value'); | |
return; | |
} | |
$this->accumulator->replaceOldOperation($diffOp); | |
} | |
public function unknownOperation() | |
{ | |
$this->accumulator->addError('Unknown diff operation cannot be applied to map element'); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment