Created
August 15, 2017 14:13
-
-
Save alexeyshockov/4fff855faa82bf23e621fb1e2ca07365 to your computer and use it in GitHub Desktop.
Object set & object map in PHP
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 | |
/** | |
* | |
* Object map proof of concept. | |
* | |
*/ | |
class Map implements IteratorAggregate, ArrayAccess, Countable | |
{ | |
/** | |
* @var SplObjectStorage | |
*/ | |
protected $storage; | |
public function __construct(callable $hasher = null) | |
{ | |
$this->storage = new SpecializedObjectStorage($hasher); | |
} | |
public function getIterator() | |
{ | |
return new ObjectStorageIterator($this->storage); | |
} | |
public function offsetExists($offset) | |
{ | |
return $this->storage->offsetExists($offset); | |
} | |
public function offsetGet($offset) | |
{ | |
return $this->storage->offsetGet($offset); | |
} | |
public function offsetSet($offset, $value) | |
{ | |
return $this->storage->offsetSet($offset, $value); | |
} | |
public function offsetUnset($offset) | |
{ | |
return $this->storage->offsetUnset($offset); | |
} | |
public function count() | |
{ | |
return $this->storage->count(); | |
} | |
} | |
class ObjectStorageIterator extends IteratorIterator | |
{ | |
/** | |
* @param SplObjectStorage $storage | |
*/ | |
public function __construct(SplObjectStorage $storage) | |
{ | |
parent::__construct($storage); | |
} | |
/** | |
* @return object | |
*/ | |
public function key() | |
{ | |
return $this->getInnerIterator()->current(); | |
} | |
/** | |
* @return mixed | |
*/ | |
public function current() | |
{ | |
return $this->getInnerIterator()->getInfo(); | |
} | |
} | |
// From 5.4... And object keys in Iterator are only from 5.6. | |
// | |
// spl_object_hash works like === for objects, it computes hash simply from all object fields (values). | |
// | |
class SpecializedObjectStorage extends SplObjectStorage | |
{ | |
/** | |
* @var callable | |
*/ | |
private $hasher; | |
/** | |
* @param callable $hasher | |
*/ | |
public function __construct(callable $hasher = null) | |
{ | |
$this->hasher = $hasher; | |
} | |
public function getHash($object) | |
{ | |
if ($this->hasher) { | |
return call_user_func($this->hasher, $object); | |
} else { | |
return $this->calculateHash($object); | |
} | |
} | |
/** | |
* Default implementation. | |
* | |
* @param object $object | |
* | |
* @return string | |
*/ | |
private function calculateHash($object) | |
{ | |
$hash = spl_object_hash($object); | |
// TODO Rewrite to pattern maching. | |
if ($object instanceof DateTime) { | |
// Simplest value object hashing. | |
// TODO Specify concrete fields to serialize. | |
return sha1(serialize($object)); | |
} | |
// TODO DateTimeZone | |
return $hash; | |
} | |
} | |
/** | |
* Helper. Like Java's Objects class (JDK 7). But return default function for given type (or object). | |
*/ | |
class Objects | |
{ | |
/** | |
* Useful for value objects: hash can be built from all immutable fields of that objects. | |
* | |
* isEqualTo() methods of corresponding objects should use === comparison to match algorithm of hashing. | |
* | |
* @param mixed ...$values | |
* | |
* @return int | |
*/ | |
public static function hash(...$values) | |
{ | |
// TODO Implement | |
// array - go deep. | |
// object: | |
// 1) Hashable? | |
// 2) Serializable? JsonSerializable? | |
// 3) DEFAULT. spl_object_hash() | |
// | |
// 0) NO. go deep. What about circular references? | |
} | |
} | |
interface Hashable | |
{ | |
/** | |
* Object hash. | |
* | |
* @return string | |
*/ | |
public function calculateHash(); | |
} | |
/* | |
* Это вообще всё полная ошибка, как всегда... Почти. В Java hash code INT, что вводит возможность коллизии. Тут можно | |
* использовать SHA1, как в распределённом GIT, при длине кторого коллизии намного менее вероятны. | |
*/ | |
/* | |
* DateTime and inherited —>getTimestamp() | |
* DateTimeZone | |
* Stringy | |
* *.getHash() - ? | |
* Тут должна быть стандартная реализация по полям как раз. Рекурсивно идти по полям и считать... | |
* trait стандартный тоже должен быть с этой реализацией! Точне, Objects.hash(field1, field2...) = возвращает функцию. | |
*/ | |
$map = new Map(); | |
$d1 = new DateTime(); | |
$d2 = new DateTime(); | |
$h1 = spl_object_hash($d1); | |
$h2 = spl_object_hash($d2); | |
// Internal object hashes are not equal. | |
var_dump($h1, $h2, $h1 === $h2); | |
// But "our" hashes are equal, because timestamp is the same. So only 1 object in the map! | |
$map[$d1] = "d1"; | |
$map[$d2] = "d2"; | |
foreach ($map as $d => $t) var_dump($d, $t); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment