Skip to content

Instantly share code, notes, and snippets.

@Taluu
Created October 28, 2013 16:18
Show Gist options
  • Select an option

  • Save Taluu/7199869 to your computer and use it in GitHub Desktop.

Select an option

Save Taluu/7199869 to your computer and use it in GitHub Desktop.
Changeset Arrays (maybe more)
<?php
namespace Taluu\Domain\ChangeSet;
/**
* Represents a change into the entity
*
* @author Rémy Gazelot <[email protected]>
* @author Baptiste Clavié <[email protected]>
*/
class Change implements ChangeInterface {
private $old; // old state
private $new; // new state
public function __construct($old, $new)
{
$this->old = $old;
$this->new = $new;
}
public function getOld()
{
return $this->old;
}
public function getNew()
{
return $this->new;
}
public function toArray($short = true)
{
return ['old' => $this->old,
'new' => $this->new];
}
}
<?php
namespace Taluu\Domain\ChangeSet;
interface ChangeInterface extends ApiAbleInterface
{
/**
* Transform this object into an array
*
* @param boolean $short short format or more verbose (more data) ?
* @return array
*/
public function toArray($short = true);
}
<?php
namespace Taluu\Domain\ChangeSet;
use \Countable,
\ArrayAccess,
\ArrayIterator,
\IteratorAggregate,
\OutOfBoundsException,
\InvalidArgumentException;
/**
* Represents a changeset
*
* @todo allow more changesets on some other things than just ApiAble objects ?
* @author Baptiste Clavié <[email protected]>
*/
class Set implements ChangeInterface, ArrayAccess, IteratorAggregate, Countable
{
private $changes = null;
public function __construct(array $old, array $new)
{
$this->compute($old, $new);
}
/**
* Retrieve a property change
*
* @param string $property
*
* @return mixed Set if it was a recursive change, Change otherwise
*/
public function getChange($property)
{
if (!$this->hasChanged($property)) {
throw new OutOfBoundsException('This property doesn\'t exist or wasn\'t changed');
}
return $this->changes[$property];
}
/**
* Test if the given property has been changed
*
* @param string $property
*
* @return boolean
*/
public function hasChanged($property)
{
return isset($this->changes[$property]);
}
/** {@inheritDoc} */
public function toArray($short = true)
{
if (true === $short) {
return array_keys($this->changes);
}
return array_map(function (ChangeInterface $change) { return $change->toArray(false); }, $this->changes);
}
/** {@inheritDoc} */
public function getIterator()
{
return new ArrayIterator($this->changes);
}
/** {@inheritDoc} */
public function offsetExists($offset)
{
return $this->hasChanged($offset);
}
/** {@inheritDoc} */
public function offsetGet($offset)
{
return $this->getChange($offset);
}
/** {@inheritDoc} */
public function offsetSet($offset, $value)
{
throw new BadMethodCallException('You cannot alter a changeset once it has been calculated');
}
/** {@inheritDoc} */
public function offsetUnset($offset)
{
throw new BadMethodCallException('You cannot alter a changeset once it has been calculated');
}
/** {@inheritDoc} */
public function count()
{
return count($this->changes);
}
/**
* Calculate the changeset between two arrays
*
* Both arrays must have the same keys and the same size
*
* @param array $old Old array
* @param array $new New array
*
* @internal for intern use only
* @return array the changes brought to the object
*/
private function compute(array $old, array $new)
{
if (array_keys($old) !== array_keys($new)) {
throw new \InvalidArgumentException('You should compare two arrays with the same keys !');
}
$this->changes = [];
foreach (array_keys($new) as $key) {
// -- if it is not the same type, then we may consider it changed
if (gettype($old[$key]) !== gettype($new[$key])) {
$this->changes[$key] = new Change($old[$key], $new[$key]);
continue;
}
// -- if it is an object (and so is new[key]), just compare the two hashes
if (is_object($old[$key])) {
if (spl_object_hash($old[$key]) !== spl_object_hash($new[$key])) {
$this->changes[$key] = new Change($old[$key], $new[$key]);
}
continue;
}
// -- if it is an array several step to check up
if (is_array($old[$key])) {
// -- not the same size ? Then it changed, completely
if (array_keys($old[$key]) !== array_keys($new[$key])) {
$this->changes[$key] = new Change($old[$key], $new[$key]);
continue;
}
// -- same size / same keys ; return only what has changed
$set = new self($old[$key], $new[$key]);
if (0 !== count($set)) {
$this->changes[$key] = $set;
}
continue;
}
// -- eventually, check if the two elements are equal
if ($old[$key] !== $new[$key]) {
$this->changes[$key] = new Change($old[$key], $new[$key]);
}
}
return $this->changes;
}
}
<?php
namespace Taluu\Domain\ChangeSet;
use \InvalidArgumentException;
/**
* Represents a snapshot of an object at a given time
*
* @author Baptiste Clavié <[email protected]>
*/
class Snapshot
{
/** @var string object's hash */
private $oid;
/** @var array object's array */
private $array;
public function __construct(ChangeInterface $object)
{
$this->array = $object->toArray(false);
$this->oid = spl_object_hash($object);
}
public function diff(self $data)
{
if ($data->oid !== $this->oid) {
throw new InvalidArgumentException('this object is not comparable with the base');
}
return new Set($this->array, $data->array);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment