From 5cc617fb1d115ff403e2cc2dae22fb05638de8a8 Mon Sep 17 00:00:00 2001 From: Bogdan Padalko Date: Thu, 27 Jul 2017 22:44:15 +0300 Subject: [PATCH] Refactor to ObjectMaps --- .scrutinizer.yml | 8 + .sensiolabs.yml | 5 + .travis.yml | 9 +- README.md | 147 +----- composer.json | 31 +- src/AbstractWeakMap.php | 169 ------- src/Bucket.php | 34 ++ .../ObjectMapExceptionInterface.php | 24 + src/Exceptions/OutOfBoundsException.php | 23 + src/Exceptions/OverflowException.php | 24 + src/Exceptions/UnexpectedValueException.php | 24 + src/HashedReference.php | 33 -- src/ObjectBiMap.php | 166 +++++++ src/ObjectBiMapInterface.php | 24 + src/ObjectMap.php | 187 ++++++++ src/ObjectMapInterface.php | 67 +++ src/ObjectTypeHintTrait.php | 29 ++ src/WeakKeyMap.php | 19 - src/WeakKeyValueMap.php | 19 - src/WeakValueMap.php | 19 - tests/AbstractObjectMapInterfaceTest.php | 277 +++++++++++ tests/BucketTest.php | 36 ++ tests/Exceptions/OutOfBoundsExceptionTest.php | 42 ++ tests/Exceptions/OverflowExceptionTest.php | 42 ++ .../UnexpectedValueExceptionTest.php | 42 ++ tests/HashedReferenceTest.php | 59 --- tests/ObjectBiMapTest.php | 273 +++++++++++ tests/ObjectMapTest.php | 28 ++ tests/WeakKeyMapTest.php | 281 ----------- tests/WeakKeyValueMapTest.php | 436 ------------------ tests/WeakValueMapTest.php | 374 --------------- tests/bootstrap.php | 4 +- 32 files changed, 1400 insertions(+), 1555 deletions(-) create mode 100644 .sensiolabs.yml delete mode 100644 src/AbstractWeakMap.php create mode 100644 src/Bucket.php create mode 100644 src/Exceptions/ObjectMapExceptionInterface.php create mode 100644 src/Exceptions/OutOfBoundsException.php create mode 100644 src/Exceptions/OverflowException.php create mode 100644 src/Exceptions/UnexpectedValueException.php delete mode 100644 src/HashedReference.php create mode 100644 src/ObjectBiMap.php create mode 100644 src/ObjectBiMapInterface.php create mode 100644 src/ObjectMap.php create mode 100644 src/ObjectMapInterface.php create mode 100644 src/ObjectTypeHintTrait.php delete mode 100644 src/WeakKeyMap.php delete mode 100644 src/WeakKeyValueMap.php delete mode 100644 src/WeakValueMap.php create mode 100644 tests/AbstractObjectMapInterfaceTest.php create mode 100644 tests/BucketTest.php create mode 100644 tests/Exceptions/OutOfBoundsExceptionTest.php create mode 100644 tests/Exceptions/OverflowExceptionTest.php create mode 100644 tests/Exceptions/UnexpectedValueExceptionTest.php delete mode 100644 tests/HashedReferenceTest.php create mode 100644 tests/ObjectBiMapTest.php create mode 100644 tests/ObjectMapTest.php delete mode 100644 tests/WeakKeyMapTest.php delete mode 100644 tests/WeakKeyValueMapTest.php delete mode 100644 tests/WeakValueMapTest.php diff --git a/.scrutinizer.yml b/.scrutinizer.yml index 87fda20..3db199b 100644 --- a/.scrutinizer.yml +++ b/.scrutinizer.yml @@ -1,2 +1,10 @@ +checks: + php: + code_rating: true + duplication: true +filter: + excluded_paths: + - "vendor/" + - "tests/" tools: external_code_coverage: true diff --git a/.sensiolabs.yml b/.sensiolabs.yml new file mode 100644 index 0000000..707755f --- /dev/null +++ b/.sensiolabs.yml @@ -0,0 +1,5 @@ +pre_composer_script: | + pecl install ref + +php_ini: | + extension=ref.so diff --git a/.travis.yml b/.travis.yml index 1b4eeb0..707dd29 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,8 @@ sudo: false language: php php: - - 7.0 + - 7.1 + - 7.2 - nightly cache: @@ -15,11 +16,11 @@ matrix: - php: nightly env: - - PHP_WEAK_VERSION=master - - PHP_WEAK_VERSION=v0.4.1 + - PHP_REF_VERSION=master + - PHP_REF_VERSION=v0.5.0 before_install: - - sh install_php_ref_ext.sh ${PHP_WEAK_VERSION} + - sh install_php_ref_ext.sh ${PHP_REF_VERSION} - echo 'extension = ref.so' >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/ref.ini before_script: diff --git a/README.md b/README.md index 7d233f6..b37334b 100644 --- a/README.md +++ b/README.md @@ -1,147 +1,44 @@ # Weak-referenced data structures for PHP based on Ref php extension -[![Build Status](https://travis-ci.org/pinepain/php-ref-lib.svg)](https://travis-ci.org/pinepain/php-ref-lib) -[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/pinepain/php-ref-lib/badges/quality-score.png)](https://scrutinizer-ci.com/g/pinepain/php-ref-lib) -[![Code Coverage](https://scrutinizer-ci.com/g/pinepain/php-ref-lib/badges/coverage.png)](https://scrutinizer-ci.com/g/pinepain/php-ref-lib) - -This library is based on [php-ref][php-ref-ext] PHP extension and provides various weak data structures: - - - [class `Ref\WeakKeyMap`](#class-refweakkeymap) - - [class `Ref\WeakValueMap`](#class-refweakvaluemap) - - [class `Ref\WeakKeyValueMap`](#class-refweakkeyvaluemap) - +[![Build Status](https://travis-ci.org/pinepain/php-object-maps.svg)](https://travis-ci.org/pinepain/php-object-maps) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/pinepain/php-object-maps/badges/quality-score.png)](https://scrutinizer-ci.com/g/pinepain/php-object-maps) +[![Code Coverage](https://scrutinizer-ci.com/g/pinepain/php-object-maps/badges/coverage.png)](https://scrutinizer-ci.com/g/pinepain/php-object-maps) +[![SensioLabsInsight](https://insight.sensiolabs.com/projects/72be40cb-1d0f-48db-b89c-c99ea007bf63/mini.png)](https://insight.sensiolabs.com/projects/72be40cb-1d0f-48db-b89c-c99ea007bf63) ## Requirements -PHP >= 7.0 and [php-ref][php-ref-ext] extension installed required. + - PHP >= 7.1 + - (optional, required only for maps weak behavior) [php-ref][php-ref-ext] extension ## Installation: - composer require pinepain/php-ref-lib - + composer require pinepain/php-object-maps ## Docs: +This library offers two main classes: `ObjectMap` and `ObjectBiMap`. +They are what their name is - classic object maps which map object keys to object values. -#### Class `Ref\WeakKeyMap` - -Mapping class that references keys weakly. Entries will be discarded when there is no longer any references to the keyleft. -This can be used to associate additional data with an object owned by other parts of an application without adding -attributes to those objects. This can be especially useful with objects that override attribute accesses. -Built on top of [`SplObjectStorage`][php-SplObjectStorage]. - -##### Example - -```php -attach($obj1, $inf1); -$map->attach($obj2, $inf2); - -var_dump($map->count()); // 2 - -// Let's destroy key -$obj1 = null; - -var_dump($map->count()); // 1 -``` - -#### Class `Ref\WeakValueMap` - -Mapping class that references values weakly. Entries will be discarded when no more reference to the value exist any more. -Built on top of [`SplObjectStorage`][php-SplObjectStorage]. - -##### Example - -```php -attach($obj1, $inf1); -$map->attach($obj2, $inf2); - -var_dump($map->count()); // 2 - -// Let's destroy value -$inf1 = null; - -var_dump($map->count()); // 1 -``` - - -#### Class `Ref\WeakKeyValueMap` - -Mapping class that references values weakly. Entries will be discarded when reference to the key or value exists any more. -Built on top of [`SplObjectStorage`][php-SplObjectStorage]. - -##### Example - -```php -attach($obj1, $inf1); -$map->attach($obj2, $inf2); - -var_dump($map->count()); // 2 - -// Let's destroy key -$obj1 = null; - -var_dump($map->count()); // 1 - -// Let's destroy value -$inf2 = null; +The key difference between +`ObjectMap` and `ObjectBiMap` is that `ObjectBiMap` require all values to be unique and it offers you to get mirrored +`ObjectBiMap` map with keys and values from source map flipped. Note, that flipped map will still maintain connection to +the original one and thus any modification to any `ObjectBiMap` in a chain will be reflected on all chain. -var_dump($map->count()); // 0 -``` +### Maps behavior -#### Caution +Both `ObjectMap` and `ObjectBiMap` offers weak variations ([php-ref][php-ref-ext] extension required) which could be specified +by passing one of `ObjectMapInterface::{WEAK_KEY,WEAK_VALUE,WEAK_KEY_VALUE}` constants to the constructor. +By default no weakness enabled. Note, that when weak behavior enable on key or/and value, their refcount won't be +incremented by map internals and thus it is possible that they will be destructed without a need be purged from map. -Because `Ref\WeakKeyMap`, `Ref\WeakValueMap` and `Ref\WeakKeyValueMap` classes are built on top of a `SplObjectStorage`, -they must not change size during iterating over it. This can be difficult to ensure because actions performed during the -iteration may cause items in the storage to vanish in a non-obvious way as a side effect of garbage collection. +`WEAK_KEY` means that key-value pair will be removed as long as key will be destructed. `WEAK_VALUE` is the same for value +and `WEAK_KEY_VALUE` will trigger removal when key or value will be destructed. +For more details see [tests](./tests). ## License -[php-ref-lib](https://github.com/pinepain/php-ref-lib) PHP library is licensed under the [MIT license](http://opensource.org/licenses/MIT). +[php-object-maps](https://github.com/pinepain/php-object-maps) PHP library is licensed under the [MIT license](http://opensource.org/licenses/MIT). [php-ref-ext]: https://github.com/pinepain/php-ref -[php-SplObjectStorage]: http://php.net/manual/en/class.splobjectstorage.php -[js-WeakMap]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/WeakMap diff --git a/composer.json b/composer.json index 4d02985..63e1cdc 100644 --- a/composer.json +++ b/composer.json @@ -1,16 +1,15 @@ { - "name": "pinepain/php-ref-lib", - "description": "Weak-referenced data structures for PHP", + "name": "pinepain/php-object-maps", + "description": "ObjectMap structures for PHP", "type": "library", "license": "MIT", "keywords": [ - "php-ref", - "weak", - "reference", - "weakref", - "weakreference", - "weak data types", - "data structure" + "map", + "bimap", + "object map", + "object bimap", + "weak object map", + "weak object bimap" ], "authors": [ { @@ -20,21 +19,23 @@ } ], "require": { - "php": "~7.0", - "ext-ref": "*" + "php": "~7.0" + }, + "suggest": { + "ext-ref": "Needed to support weak map variations" }, "require-dev": { - "phpunit/phpunit": "^5.1", - "pinepain/php-ref-stubs": "~0.4.0" + "phpunit/phpunit": "^6.2", + "pinepain/php-ref-stubs": "~0.4.1" }, "autoload": { "psr-4": { - "Ref\\": "./src" + "Pinepain\\ObjectMaps\\": "./src" } }, "autoload-dev": { "psr-4": { - "Ref\\Tests\\": "./tests" + "Pinepain\\ObjectMaps\\Tests\\": "./tests" } } } diff --git a/src/AbstractWeakMap.php b/src/AbstractWeakMap.php deleted file mode 100644 index 0861470..0000000 --- a/src/AbstractWeakMap.php +++ /dev/null @@ -1,169 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref; - -use RuntimeException; -use SplObjectStorage; - -abstract class AbstractWeakMap extends SplObjectStorage -{ - const WEAK_KEY = 1; - const WEAK_VAL = 2; - - protected $behavior = 0; - - /** {@inheritdoc} */ - public function attach($object, $data = null) - { - if ($this->behavior & self::WEAK_KEY) { - $object = $this->buildWeakKey($object); - } - - if ($this->behavior & self::WEAK_VAL) { - $data = $this->buildWeakValue($object, $data); - } - - parent::attach($object, $data); - } - - /** {@inheritdoc} */ - public function current() - { - $object = parent::current(); - - if ($this->behavior & self::WEAK_KEY) { - $object = $this->extractWeakObject($object); - } - - /* In some rare cases (it should never happen normally) orphaned weak reference may occurs in storage, so - * so current object here MAY be null - */ - - return $object; - } - - /** {@inheritdoc} */ - public function addAll($storage) - { - foreach ($storage as $obj) { - $this->attach($obj, $storage[$obj]); - } - } - - /** {@inheritdoc} */ - public function removeAllExcept($storage) - { - $pending_removal = new \SplObjectStorage(); - - foreach ($this as $obj) { - if (!$storage->contains($obj)) { - $pending_removal->attach($obj); - } - } - - $this->removeAll($pending_removal); - } - - /** {@inheritdoc} */ - public function getHash($object) - { - if ($this->behavior & self::WEAK_KEY) { - if ($object instanceof HashedReference) { - return $object->getHash(); - } - } - - return parent::getHash($object); - } - - /** {@inheritdoc} */ - public function getInfo() - { - $info = parent::getInfo(); - - if ($this->behavior & self::WEAK_VAL) { - $info = $this->extractWeakObject($info); - } - - return $info; - } - - /** {@inheritdoc} */ - public function setInfo($data) - { - $object = $this->current(); - - if (!$object) { - return; // nothing to do - } - - if ($this->behavior & self::WEAK_VAL) { - $data = $this->buildWeakValue($object, $data); - } - - parent::setInfo($data); - } - - /** {@inheritdoc} */ - public function offsetSet($object, $data = null) - { - $this->attach($object, $data); - } - - /** {@inheritdoc} */ - public function offsetGet($object) - { - $info = parent::offsetGet($object); - - if ($this->behavior & self::WEAK_VAL) { - $info = $this->extractWeakObject($info); - } - - return $info; - } - - protected function validateInfo($data) - { - if (!is_object($data)) { - throw new RuntimeException(static::class . ' expects data to be object, ' . gettype($data) . ' given'); - } - } - - protected function extractWeakObject($object) - { - if ($object instanceof WeakReference) { - $object = $object->get(); - } - - return $object; - } - - protected function buildWeakKey($object) - { - $object = new HashedReference($object, [$this, 'detach'], 'spl_object_hash'); - - return $object; - } - - protected function buildWeakValue($object, $data) - { - $this->validateInfo($data); - - $data = new WeakReference($data, function () use ($object) { - $this->detach($object); - }); - - return $data; - } -} diff --git a/src/Bucket.php b/src/Bucket.php new file mode 100644 index 0000000..7d6b1c4 --- /dev/null +++ b/src/Bucket.php @@ -0,0 +1,34 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps; + +/** + * @internal + */ +final class Bucket +{ + public $key; + public $value; + + /** + * @param object $key + * @param object $value + */ + public function __construct($key, $value) + { + $this->key = $key; + $this->value = $value; + } +} diff --git a/src/Exceptions/ObjectMapExceptionInterface.php b/src/Exceptions/ObjectMapExceptionInterface.php new file mode 100644 index 0000000..a86b50f --- /dev/null +++ b/src/Exceptions/ObjectMapExceptionInterface.php @@ -0,0 +1,24 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps\Exceptions; + + + +use Throwable; + + +interface ObjectMapExceptionInterface extends Throwable +{ +} diff --git a/src/Exceptions/OutOfBoundsException.php b/src/Exceptions/OutOfBoundsException.php new file mode 100644 index 0000000..d550e8d --- /dev/null +++ b/src/Exceptions/OutOfBoundsException.php @@ -0,0 +1,23 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps\Exceptions; + + +use OutOfBoundsException as CorePHPOutOfBoundsException; + + +class OutOfBoundsException extends CorePHPOutOfBoundsException implements ObjectMapExceptionInterface +{ +} diff --git a/src/Exceptions/OverflowException.php b/src/Exceptions/OverflowException.php new file mode 100644 index 0000000..dd4596f --- /dev/null +++ b/src/Exceptions/OverflowException.php @@ -0,0 +1,24 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps\Exceptions; + + +use OverflowException as CorePHPOverflowException; + + +class OverflowException extends CorePHPOverflowException implements ObjectMapExceptionInterface +{ +} + diff --git a/src/Exceptions/UnexpectedValueException.php b/src/Exceptions/UnexpectedValueException.php new file mode 100644 index 0000000..05c7211 --- /dev/null +++ b/src/Exceptions/UnexpectedValueException.php @@ -0,0 +1,24 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps\Exceptions; + + +use UnexpectedValueException as CorePHPUnexpectedValueException; + + +class UnexpectedValueException extends CorePHPUnexpectedValueException implements ObjectMapExceptionInterface +{ +} + diff --git a/src/HashedReference.php b/src/HashedReference.php deleted file mode 100644 index 6fb32ab..0000000 --- a/src/HashedReference.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref; - -use function spl_object_hash; - -class HashedReference extends WeakReference -{ - private $hash; - - public function __construct($referent, callable $notify = null, callable $hash_function = null) - { - parent::__construct($referent, $notify); - - $this->hash = $hash_function ? $hash_function($referent) : spl_object_hash($referent); - } - - public function getHash() : string - { - return $this->hash; - } -} diff --git a/src/ObjectBiMap.php b/src/ObjectBiMap.php new file mode 100644 index 0000000..c4f4ae1 --- /dev/null +++ b/src/ObjectBiMap.php @@ -0,0 +1,166 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps; + + +use Pinepain\ObjectMaps\Exceptions\OutOfBoundsException; +use Pinepain\ObjectMaps\Exceptions\OverflowException; + + +class ObjectBiMap implements ObjectBiMapInterface +{ + use ObjectTypeHintTrait; + + protected $behavior = self::DEFAULT; + + /** + * @var ObjectMapInterface + */ + protected $keys; + /** + * @var ObjectMapInterface + */ + protected $values; + + /** + * @param int $behavior + */ + public function __construct(int $behavior = self::DEFAULT) + { + $key_behavior = 0; + $value_behavior = 0; + + if ($behavior & self::WEAK_KEY) { + $key_behavior |= self::WEAK_KEY; + $value_behavior |= self::WEAK_VALUE; + } + + if ($behavior & self::WEAK_VALUE) { + $key_behavior |= self::WEAK_VALUE; + $value_behavior |= self::WEAK_KEY; + } + + $this->keys = new ObjectMap($key_behavior); + $this->values = new ObjectMap($value_behavior); + + $this->behavior = $behavior; + } + + /** + * {@inheritdoc} + */ + public function values(): ObjectBiMapInterface + { + $new_behavior = 0; + + if ($this->behavior & self::WEAK_KEY) { + $new_behavior |= self::WEAK_VALUE; + } + + if ($this->behavior & self::WEAK_VALUE) { + $new_behavior |= self::WEAK_KEY; + } + + $new = new static($new_behavior); + + $new->keys = $this->values; + $new->values = $this->keys; + + return $new; + } + + /** + * {@inheritdoc} + */ + public function put($key, $value) + { + $this->assertObject($key, 'Key'); + + if ($this->keys->has($key)) { + throw new OverflowException('Value with such key already exists'); + } + + $this->assertObject($value, 'Value'); + + if ($this->values->has($value)) { + // UNEXPECTED + throw new OverflowException('Key with such value already exists'); + } + + $this->keys->put($key, $value); + $this->values->put($value, $key); + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + $this->assertObject($key, 'Key'); + + return $this->keys->get($key); + } + + /** + * {@inheritdoc} + */ + + public function has($key): bool + { + $this->assertObject($key, 'Key'); + + return $this->keys->has($key); + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + $this->assertObject($key, 'Key'); + + if (!$this->keys->has($key)) { + throw new OutOfBoundsException('Value with such key not found'); + } + + $value = $this->keys->remove($key); + + if (!$this->values->has($value)) { + // UNEXPECTED + throw new OutOfBoundsException('Key with such value not found'); + } + + $this->values->remove($value); + + return $value; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->keys); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->keys->clear(); + $this->values->clear(); + } +} diff --git a/src/ObjectBiMapInterface.php b/src/ObjectBiMapInterface.php new file mode 100644 index 0000000..0f0cdb3 --- /dev/null +++ b/src/ObjectBiMapInterface.php @@ -0,0 +1,24 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps; + + +interface ObjectBiMapInterface extends ObjectMapInterface +{ + /** + * @return ObjectBiMapInterface + */ + public function values(): ObjectBiMapInterface; +} diff --git a/src/ObjectMap.php b/src/ObjectMap.php new file mode 100644 index 0000000..8c640ef --- /dev/null +++ b/src/ObjectMap.php @@ -0,0 +1,187 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps; + + +use Ref\WeakReference; + +use Pinepain\ObjectMaps\Exceptions\OutOfBoundsException; +use Pinepain\ObjectMaps\Exceptions\OverflowException; + +use function spl_object_hash; + + +class ObjectMap implements ObjectMapInterface +{ + use ObjectTypeHintTrait; + + protected $behavior = self::DEFAULT; + + /** + * @var Bucket[] + */ + protected $keys = []; + + /** + * @param int $behavior + */ + public function __construct(int $behavior = self::DEFAULT) + { + $this->behavior = $behavior; + } + + /** + * {@inheritdoc} + */ + public function put($key, $value) + { + $this->assertObject($key, 'Key'); + $this->assertObject($value, 'Value'); // while we may associate non-object value, for interface compatibility we don't do that + + $hash = $this->getHash($key); + + if (isset($this->keys[$hash])) { + throw new OverflowException('Value with such key already exists'); + } + + $bucket = $this->createBucket($key, $value, $hash); + + $this->keys[$hash] = $bucket; + } + + /** + * {@inheritdoc} + */ + public function get($key) + { + $this->assertObject($key, 'Key'); + + $hash = $this->getHash($key); + + if (!isset($this->keys[$hash])) { + throw new OutOfBoundsException('Value with such key not found'); + } + + return $this->keys[$hash]->value; + } + + /** + * {@inheritdoc} + */ + public function has($key): bool + { + $this->assertObject($key, 'Key'); + + $hash = $this->getHash($key); + + return isset($this->keys[$hash]); + } + + /** + * {@inheritdoc} + */ + public function remove($key) + { + $this->assertObject($key, 'Key'); + + $hash = $this->getHash($key); + + if (!isset($this->keys[$hash])) { + throw new OutOfBoundsException('Value with such key not found'); + } + + $bucket = $this->keys[$hash]; + + $this->doRemove($hash); + + return $bucket->value; + } + + /** + * {@inheritdoc} + */ + public function count() + { + return count($this->keys); + } + + /** + * {@inheritdoc} + */ + public function clear() + { + $this->keys = []; + } + + /** + * @param object $value + * + * @return string + */ + protected function getHash($value) + { + return spl_object_hash($value); + } + + /** + * @param string $hash + * + * @return void + */ + protected function doRemove(string $hash) + { + unset($this->keys[$hash]); + } + + /** + * @param object $key + * @param object $value + * @param string $hash + * + * @return Bucket + */ + protected function createBucket($key, $value, string $hash): Bucket + { + if ($this->behavior & self::WEAK_KEY) { + $key = $this->createReference($key, $hash); + } + + if ($this->behavior & self::WEAK_VALUE) { + $value = $this->createReference($value, $hash); + } + + return new Bucket($key, $value); + } + + /** + * @param $obj + * @param string $hash + * + * @return WeakReference + */ + protected function createReference($obj, string $hash): WeakReference + { + return new WeakReference($obj, function () use ($hash) { + $this->doRemove($hash); + }); + } +} + + + + + + + diff --git a/src/ObjectMapInterface.php b/src/ObjectMapInterface.php new file mode 100644 index 0000000..be7f937 --- /dev/null +++ b/src/ObjectMapInterface.php @@ -0,0 +1,67 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps; + +use Countable; +use Pinepain\ObjectMaps\Exceptions\OutOfBoundsException; +use Pinepain\ObjectMaps\Exceptions\OverflowException; + + +interface ObjectMapInterface extends Countable +{ + const DEFAULT = 0; + const WEAK_KEY = 1 << 0; + const WEAK_VALUE = 1 << 1; + const WEAK_KEY_VALUE = self::WEAK_KEY | self::WEAK_VALUE; + + /** + * @param object $key + * @param object $value + * + * @throws OverflowException + * @return void + */ + public function put($key, $value); + + /** + * @param object $key + * + * @throws OutOfBoundsException + * + * @return object + */ + public function get($key); + + /** + * @param object $key + * + * @return bool + */ + public function has($key): bool; + + /** + * @param object $key + * + * @throws OutOfBoundsException + * + * @return object + */ + public function remove($key); + + /** + * @return void + */ + public function clear(); +} diff --git a/src/ObjectTypeHintTrait.php b/src/ObjectTypeHintTrait.php new file mode 100644 index 0000000..6ba3a56 --- /dev/null +++ b/src/ObjectTypeHintTrait.php @@ -0,0 +1,29 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + +namespace Pinepain\ObjectMaps; + + +use function is_object; +use Pinepain\ObjectMaps\Exceptions\UnexpectedValueException; + + +trait ObjectTypeHintTrait +{ + protected function assertObject($value, string $name) + { + if (!is_object($value)) { + throw new UnexpectedValueException("{$name} is not an object"); + } + } +} diff --git a/src/WeakKeyMap.php b/src/WeakKeyMap.php deleted file mode 100644 index 9dbda23..0000000 --- a/src/WeakKeyMap.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref; - -class WeakKeyMap extends AbstractWeakMap -{ - protected $behavior = self::WEAK_KEY; -} diff --git a/src/WeakKeyValueMap.php b/src/WeakKeyValueMap.php deleted file mode 100644 index 3c0220b..0000000 --- a/src/WeakKeyValueMap.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref; - -class WeakKeyValueMap extends AbstractWeakMap -{ - protected $behavior = self::WEAK_KEY | self::WEAK_VAL; -} diff --git a/src/WeakValueMap.php b/src/WeakValueMap.php deleted file mode 100644 index 6d94ef7..0000000 --- a/src/WeakValueMap.php +++ /dev/null @@ -1,19 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref; - -class WeakValueMap extends AbstractWeakMap -{ - protected $behavior = self::WEAK_VAL; -} diff --git a/tests/AbstractObjectMapInterfaceTest.php b/tests/AbstractObjectMapInterfaceTest.php new file mode 100644 index 0000000..31c75f4 --- /dev/null +++ b/tests/AbstractObjectMapInterfaceTest.php @@ -0,0 +1,277 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps\Tests; + +use PHPUnit\Framework\TestCase; +use Pinepain\ObjectMaps\ObjectBiMapInterface; +use Pinepain\ObjectMaps\ObjectMapInterface; +use stdClass; + + +abstract class AbstractObjectMapInterfaceTest extends TestCase +{ + /** + * @param int $behavior + * + * @return ObjectMapInterface | ObjectBiMapInterface + */ + abstract public function buildMap(int $behavior = ObjectMapInterface::DEFAULT); + + public function testMap() + { + $map = $this->buildMap(); + + $key = new stdClass(); + $value = new stdClass(); + + $this->assertSame(0, $map->count()); + $map->put($key, $value); + $this->assertSame(1, $map->count()); + + $value = null; + $this->assertSame(1, $map->count()); + + $key = null; + $this->assertSame(1, $map->count()); + } + + public function testWeakKeyMap() + { + $map = $this->buildMap(ObjectMapInterface::WEAK_KEY); + + $key = new stdClass(); + $value = new stdClass(); + + $this->assertSame(0, $map->count()); + $map->put($key, $value); + $this->assertSame(1, $map->count()); + + $value = null; + $this->assertSame(1, $map->count()); + + $key = null; + $this->assertSame(0, $map->count()); + } + + public function testWeakValueMap() + { + $map = $this->buildMap(ObjectMapInterface::WEAK_VALUE); + + $key = new stdClass(); + $value = new stdClass(); + + $this->assertSame(0, $map->count()); + $map->put($key, $value); + $this->assertSame(1, $map->count()); + + $key = null; + $this->assertSame(1, $map->count()); + + $value = null; + $this->assertSame(0, $map->count()); + } + + public function testWeakKeyValueMap() + { + $map = $this->buildMap(ObjectMapInterface::WEAK_KEY_VALUE); + + $key = new stdClass(); + $value = new stdClass(); + + $this->assertSame(0, $map->count()); + $map->put($key, $value); + $this->assertSame(1, $map->count()); + + $key = null; + $this->assertSame(0, $map->count()); + + + $key = new stdClass(); + $value = new stdClass(); + + $this->assertSame(0, $map->count()); + $map->put($key, $value); + $this->assertSame(1, $map->count()); + + $value = null; + $this->assertSame(0, $map->count()); + } + + public function testPutAndGet() + { + $map = $this->buildMap(); + + $key = new stdClass(); + $value = new stdClass(); + + $map->put($key, $value); + $this->assertSame($value, $map->get($key)); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\UnexpectedValueException + * @expectedExceptionMessage Key is not an object + */ + public function testPutKeyNotAnObjectFails() + { + $map = $this->buildMap(); + + $map->put('foo', 'bar'); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\UnexpectedValueException + * @expectedExceptionMessage Value is not an object + */ + public function testPutValueNotAnObjectFails() + { + $map = $this->buildMap(); + + $map->put(new stdClass(), 'bar'); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\OverflowException + * @expectedExceptionMessage Value with such key already exists + */ + public function testPutExistentKeyFails() + { + $map = $this->buildMap(); + + $key = new stdClass(); + $value = new stdClass(); + + $map->put($key, $value); + $map->put($key, $value); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\UnexpectedValueException + * @expectedExceptionMessage Key is not an object + */ + public function testGetByNonObjectFails() + { + $map = $this->buildMap(); + + $map->get('test'); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\OutOfBoundsException + * @expectedExceptionMessage Value with such key not found + */ + public function testGetNonexistentKeyFails() + { + $map = $this->buildMap(); + + $map->get(new stdClass()); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\UnexpectedValueException + * @expectedExceptionMessage Key is not an object + */ + public function testHasByNonObjectFails() + { + $map = $this->buildMap(); + + $map->has('test'); + } + + public function testHasNonexistentKey() + { + $map = $this->buildMap(); + + $this->assertFalse($map->has(new stdClass())); + } + + public function testHasExistentKey() + { + $map = $this->buildMap(); + + $key = new stdClass(); + $map->put($key, new stdClass()); + + $this->assertTrue($map->has($key)); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\UnexpectedValueException + * @expectedExceptionMessage Key is not an object + */ + public function testRemoveByNonObjectFails() + { + $map = $this->buildMap(); + + $map->remove('test'); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\OutOfBoundsException + * @expectedExceptionMessage Value with such key not found + */ + public function testRemoveNonexistentKeyFails() + { + $map = $this->buildMap(); + + $map->remove(new stdClass()); + } + + public function testRemove() + { + $map = $this->buildMap(); + + $key = new stdClass(); + $map->put($key, new stdClass()); + + $this->assertTrue($map->has($key)); + + $map->remove($key); + + $this->assertFalse($map->has($key)); + } + + public function testCount() + { + $map = $this->buildMap(); + + $this->assertSame(0, $map->count()); + + $key = new stdClass(); + $map->put($key, new stdClass()); + + $this->assertSame(1, $map->count()); + + $map->remove($key); + + $this->assertSame(0, $map->count()); + } + + public function testClear() + { + $map = $this->buildMap(); + + $this->assertSame(0, $map->count()); + + $map->put(new stdClass(), new stdClass()); + $this->assertSame(1, $map->count()); + $map->put(new stdClass(), new stdClass()); + $this->assertSame(2, $map->count()); + + $map->clear(); + + $this->assertSame(0, $map->count()); + } +} diff --git a/tests/BucketTest.php b/tests/BucketTest.php new file mode 100644 index 0000000..4f5fc53 --- /dev/null +++ b/tests/BucketTest.php @@ -0,0 +1,36 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + + +namespace Pinepain\ObjectMaps\Tests; + + +use Pinepain\ObjectMaps\Bucket; +use PHPUnit\Framework\TestCase; +use stdClass; + + +class BucketTest extends TestCase +{ + public function test() + { + $key = new stdClass(); + $value = new stdClass(); + + $bucket = new Bucket($key, $value); + + $this->assertSame($key, $bucket->key); + $this->assertSame($value, $bucket->value); + } +} diff --git a/tests/Exceptions/OutOfBoundsExceptionTest.php b/tests/Exceptions/OutOfBoundsExceptionTest.php new file mode 100644 index 0000000..48b01b1 --- /dev/null +++ b/tests/Exceptions/OutOfBoundsExceptionTest.php @@ -0,0 +1,42 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + +namespace Pinepain\ObjectMaps\Tests\Exceptions; + + +use PHPUnit\Framework\TestCase; + +use Pinepain\ObjectMaps\Exceptions\OverflowException; +use Pinepain\ObjectMaps\Exceptions\ObjectMapExceptionInterface; + + +class OverflowExceptionTest extends TestCase +{ + public function testCatchExceptionByInterface() + { + try { + throw new OverflowException('test'); + } catch (ObjectMapExceptionInterface $e) { + $this->assertSame('test', $e->getMessage()); + } + } + + public function testCatchExceptionByCoreException() + { + try { + throw new OverflowException('test'); + } catch (\OverflowException $e) { + $this->assertSame('test', $e->getMessage()); + } + } +} diff --git a/tests/Exceptions/OverflowExceptionTest.php b/tests/Exceptions/OverflowExceptionTest.php new file mode 100644 index 0000000..1eed82c --- /dev/null +++ b/tests/Exceptions/OverflowExceptionTest.php @@ -0,0 +1,42 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + +namespace Pinepain\ObjectMaps\Tests\Exceptions; + + +use PHPUnit\Framework\TestCase; + +use Pinepain\ObjectMaps\Exceptions\OutOfBoundsException; +use Pinepain\ObjectMaps\Exceptions\ObjectMapExceptionInterface; + + +class OutOfBoundsExceptionTest extends TestCase +{ + public function testCatchExceptionByInterface() + { + try { + throw new OutOfBoundsException('test'); + } catch (ObjectMapExceptionInterface $e) { + $this->assertSame('test', $e->getMessage()); + } + } + + public function testCatchExceptionByCoreException() + { + try { + throw new OutOfBoundsException('test'); + } catch (\OutOfBoundsException $e) { + $this->assertSame('test', $e->getMessage()); + } + } +} diff --git a/tests/Exceptions/UnexpectedValueExceptionTest.php b/tests/Exceptions/UnexpectedValueExceptionTest.php new file mode 100644 index 0000000..9591ae4 --- /dev/null +++ b/tests/Exceptions/UnexpectedValueExceptionTest.php @@ -0,0 +1,42 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + +namespace Pinepain\ObjectMaps\Tests\Exceptions; + + +use PHPUnit\Framework\TestCase; + +use Pinepain\ObjectMaps\Exceptions\UnexpectedValueException; +use Pinepain\ObjectMaps\Exceptions\ObjectMapExceptionInterface; + + +class UnexpectedValueExceptionTest extends TestCase +{ + public function testCatchExceptionByInterface() + { + try { + throw new UnexpectedValueException('test'); + } catch (ObjectMapExceptionInterface $e) { + $this->assertSame('test', $e->getMessage()); + } + } + + public function testCatchExceptionByCoreException() + { + try { + throw new UnexpectedValueException('test'); + } catch (\UnexpectedValueException $e) { + $this->assertSame('test', $e->getMessage()); + } + } +} diff --git a/tests/HashedReferenceTest.php b/tests/HashedReferenceTest.php deleted file mode 100644 index 046d928..0000000 --- a/tests/HashedReferenceTest.php +++ /dev/null @@ -1,59 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref\Tests; - -use PHPUnit_Framework_TestCase; -use stdClass; -use Ref\HashedReference; - -class HashedReferenceTest extends PHPUnit_Framework_TestCase -{ - public function test() - { - $obj = new stdClass(); - $obj_hash = 'test_hash'; - - $n = $this->getMockBuilder(stdClass::class) - ->setMethods(['notify', 'hash']) - ->getMock(); - - $n->expects($this->once()) - ->method('notify'); - - - $n->expects($this->once()) - ->method('hash') - ->willReturn($obj_hash); - - $notify = function () use (&$n) { - return $n->notify(); - }; - - $hash = function () use (&$n) { - return $n->hash(); - }; - - $wr = new HashedReference($obj, $notify, $hash); - - $this->assertSame($obj, $wr->get()); - $this->assertTrue($wr->valid()); - $this->assertSame($obj_hash, $wr->getHash()); - - $obj = null; - - $this->assertNull($wr->get()); - $this->assertFalse($wr->valid()); - $this->assertSame($obj_hash, $wr->getHash()); - } -} diff --git a/tests/ObjectBiMapTest.php b/tests/ObjectBiMapTest.php new file mode 100644 index 0000000..9ca195f --- /dev/null +++ b/tests/ObjectBiMapTest.php @@ -0,0 +1,273 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps\Tests; + + +use Pinepain\ObjectMaps\ObjectBiMap; +use Pinepain\ObjectMaps\ObjectBiMapInterface; +use Pinepain\ObjectMaps\ObjectMap; +use Pinepain\ObjectMaps\ObjectMapInterface; +use stdClass; + + +class ObjectBiMapTest extends AbstractObjectMapInterfaceTest +{ + public function buildMap(int $behavior = ObjectMapInterface::DEFAULT) + { + return new ObjectBiMap($behavior); + } + + public function testValuesForRegularMap() + { + $map = $this->buildMap(); + + $key = new stdClass(); + $value = new stdClass(); + + $map->put($key, $value); + + $this->assertTrue($map->has($key)); + $this->assertFalse($map->has($value)); + + $vmap = $map->values(); + + $this->assertInstanceOf(ObjectBiMapInterface::class, $vmap); + + $this->assertFalse($vmap->has($key)); + $this->assertTrue($vmap->has($value)); + + $map->clear(); + + $this->assertSame(0, $map->count()); + $this->assertSame(0, $vmap->count()); + + $vmap->put($key, $value); + + $this->assertSame(1, $vmap->count()); + $this->assertSame(1, $map->count()); + + $this->assertFalse($map->has($key)); + $this->assertTrue($map->has($value)); + + $key = null; + $this->assertSame(1, $vmap->count()); + $this->assertSame(1, $map->count()); + + $value = null; + $this->assertSame(1, $vmap->count()); + $this->assertSame(1, $map->count()); + } + + public function testValuesForWeakKeyMap() + { + $map = $this->buildMap(ObjectBiMapInterface::WEAK_KEY); + + $key = new stdClass(); + $value = new stdClass(); + + $map->put($key, $value); + + $this->assertSame(1, $map->count()); + + $vmap = $map->values(); + + $this->assertInstanceOf(ObjectBiMapInterface::class, $vmap); + + $this->assertFalse($vmap->has($key)); + $this->assertTrue($vmap->has($value)); + + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $value = null; + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $key = null; + $this->assertSame(0, $map->count()); + $this->assertSame(0, $vmap->count()); + + $key = new stdClass(); + $value = new stdClass(); + + $vmap->put($key, $value); + + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $key = null; + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $value = null; + $this->assertSame(0, $map->count()); + $this->assertSame(0, $vmap->count()); + } + + public function testValuesForWeakValueMap() + { + $map = $this->buildMap(ObjectBiMapInterface::WEAK_VALUE); + + $key = new stdClass(); + $value = new stdClass(); + + $map->put($key, $value); + + $this->assertSame(1, $map->count()); + + $vmap = $map->values(); + + $this->assertInstanceOf(ObjectBiMapInterface::class, $vmap); + + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $key = null; + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $value = null; + $this->assertSame(0, $map->count()); + $this->assertSame(0, $vmap->count()); + + $key = new stdClass(); + $value = new stdClass(); + + $vmap->put($key, $value); + + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $value = null; + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $key = null; + $this->assertSame(0, $map->count()); + $this->assertSame(0, $vmap->count()); + } + + + public function testValuesForWeakKeyValueMap() + { + $map = $this->buildMap(ObjectBiMapInterface::WEAK_KEY_VALUE); + + $key_1 = new stdClass(); + $value_1 = new stdClass(); + + $key_2 = new stdClass(); + $value_2 = new stdClass(); + + $map->put($key_1, $value_1); + $map->put($key_2, $value_2); + + $this->assertSame(2, $map->count()); + + $vmap = $map->values(); + + $this->assertInstanceOf(ObjectBiMapInterface::class, $vmap); + + $this->assertSame(2, $map->count()); + $this->assertSame(2, $vmap->count()); + + $key_1 = null; + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $value_2 = null; + $this->assertSame(0, $map->count()); + $this->assertSame(0, $vmap->count()); + + $key_1 = new stdClass(); + $value_1 = new stdClass(); + + $key_2 = new stdClass(); + $value_2 = new stdClass(); + + $vmap->put($key_1, $value_1); + $vmap->put($key_2, $value_2); + + $this->assertSame(2, $map->count()); + $this->assertSame(2, $vmap->count()); + + $key_1 = null; + $this->assertSame(1, $map->count()); + $this->assertSame(1, $vmap->count()); + + $value_2 = null; + $this->assertSame(0, $map->count()); + $this->assertSame(0, $vmap->count()); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\OverflowException + * @expectedExceptionMessage Key with such value already exists + */ + public function testKeyValuesOutOfSyncPutFails() + { + $key_1 = new stdClass(); + $value_1 = new stdClass(); + + $key_2 = new stdClass(); + $value_2 = new stdClass(); + + $keys_map = new ObjectMap(); + $values_map = new ObjectMap(); + + $keys_map->put($key_1, $value_1); + $values_map->put($value_2, $key_2); + + $map = new class($keys_map, $values_map) extends ObjectBiMap + { + public function __construct(ObjectMapInterface $keys, ObjectMapInterface $values) + { + $this->keys = $keys; + $this->values = $values; + } + }; + + $map->put($key_2, $value_2); + } + + /** + * @expectedException \Pinepain\ObjectMaps\Exceptions\OutOfBoundsException + * @expectedExceptionMessage Key with such value not found + */ + public function testKeyValuesOutOfSyncRemoveFails() + { + $key_1 = new stdClass(); + $value_1 = new stdClass(); + + $key_2 = new stdClass(); + $value_2 = new stdClass(); + + $keys_map = new ObjectMap(); + $values_map = new ObjectMap(); + + $keys_map->put($key_1, $value_1); + $values_map->put($value_2, $key_2); + + $map = new class($keys_map, $values_map) extends ObjectBiMap + { + public function __construct(ObjectMapInterface $keys, ObjectMapInterface $values) + { + $this->keys = $keys; + $this->values = $values; + } + }; + + $map->remove($key_1); + } +} diff --git a/tests/ObjectMapTest.php b/tests/ObjectMapTest.php new file mode 100644 index 0000000..bd85b78 --- /dev/null +++ b/tests/ObjectMapTest.php @@ -0,0 +1,28 @@ + + * + * Licensed under the MIT license: http://opensource.org/licenses/MIT + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code or visit http://opensource.org/licenses/MIT + */ + + +namespace Pinepain\ObjectMaps\Tests; + + +use Pinepain\ObjectMaps\ObjectMap; +use Pinepain\ObjectMaps\ObjectMapInterface; + + +class ObjectMapTest extends AbstractObjectMapInterfaceTest +{ + public function buildMap(int $behavior = ObjectMapInterface::DEFAULT) + { + return new ObjectMap($behavior); + } +} diff --git a/tests/WeakKeyMapTest.php b/tests/WeakKeyMapTest.php deleted file mode 100644 index fbeeaea..0000000 --- a/tests/WeakKeyMapTest.php +++ /dev/null @@ -1,281 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref\Tests; - -use PHPUnit_Framework_TestCase; -use Ref\HashedReference; -use Ref\NotifierException; -use Ref\WeakKeyMap; -use Ref\WeakReference; -use SplObjectStorage; -use stdClass; - -class WeakKeyMapTest extends PHPUnit_Framework_TestCase -{ - public function testGetHash() - { - $map = new WeakKeyMap(); - - $obj = new stdClass(); - - // we need this to update referent object handlers HashTable so it changes it value and gives us new hash, now consistent - new WeakReference($obj); - - $expected_hash = spl_object_hash($obj); - - $this->assertSame($expected_hash, $map->getHash($obj)); - - $wr = new HashedReference($obj); - - $this->assertSame($expected_hash, $map->getHash($wr)); - - $obj = null; - - $this->assertNull($wr->get()); - $this->assertSame($expected_hash, $map->getHash($wr)); - } - - public function testAttach() - { - $map = new WeakKeyMap(); - - $obj1 = new stdClass(); - $map->attach($obj1); - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertNull($map[$obj1]); - - $obj2 = new stdClass(); - $map->attach($obj2, 'data'); - - $this->assertCount(2, $map); - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($map[$obj2], 'data'); - } - - public function testOffsetSet() - { - $map = new WeakKeyMap(); - - $obj1 = new stdClass(); - $map[$obj1] = null; - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertNull($map[$obj1]); - - $obj2 = new stdClass(); - $map[$obj2] = 'data'; - - $this->assertCount(2, $map); - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($map[$obj2], 'data'); - - $obj2 = null; - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - - $obj1 = null; - $this->assertCount(0, $map); - } - - public function testGetCurrent() - { - $map = new WeakKeyMap(); - - $obj1 = new stdClass(); - $inf1 = '$obj1'; - - $obj2 = new stdClass(); - $inf2 = '$obj2'; - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - $map->rewind(); - $this->assertSame($obj1, $map->current()); - $this->assertSame($inf1, $map->getInfo()); - $map->next(); - $this->assertSame($obj2, $map->current()); - $this->assertSame($inf2, $map->getInfo()); - } - - public function testOffsetUnset() - { - $map = new WeakKeyMap(); - - $obj1 = new stdClass(); - $map[$obj1] = null; - $obj2 = new stdClass(); - $map[$obj2] = 'data'; - - $this->assertCount(2, $map); - - unset($map[$obj1]); - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj2)); - - unset($map[$obj2]); - $this->assertCount(0, $map); - } - - public function testObjectRemoved() - { - $map = new WeakKeyMap(); - - $obj1 = new stdClass(); - $inf1 = '$obj1'; - - $obj2 = new stdClass(); - $inf2 = '$obj2'; - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - $map->rewind(); - $this->assertSame($obj1, $map->current()); - $this->assertSame($inf1, $map->getInfo()); - $map->next(); - $this->assertSame($obj2, $map->current()); - $this->assertSame($inf2, $map->getInfo()); - - $obj1 = null; - - $this->assertCount(1, $map); - $map->rewind(); - $this->assertSame($obj2, $map->current()); - $this->assertSame($inf2, $map->getInfo()); - } - - - public function testItemRemovedEvenWhenExceptionThrownInDtor() - { - $map = new WeakKeyMap(); - - $obj1 = new class - { - function __destruct() - { - throw new \Exception('Prevent weakref handler to be executed'); - } - }; - - $inf1 = '$obj1'; - - $obj2 = new stdClass(); - $inf2 = '$obj2'; - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - try { - $obj1 = null; - } catch (NotifierException $e) { - $this->assertSame('One or more exceptions thrown during notifiers calling', $e->getMessage()); - $this->assertSame('Prevent weakref handler to be executed', $e->getExceptions()[0]->getMessage()); - } - - $this->assertCount(1, $map); - $map->rewind(); - $this->assertSame($obj2, $map->current()); - $this->assertSame($inf2, $map->getInfo()); - } - - - public function testAddAll() - { - $map = new WeakKeyMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $obj2 = new stdClass(); - - $storage->attach($obj1); - $storage->attach($obj2); - - $this->assertCount(2, $storage); - - $map->addAll($storage); - - $this->assertCount(2, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - - $storage = null; - - $obj1 = null; - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj2)); - - - $obj2 = null; - $this->assertCount(0, $map); - } - - public function testRemoveAll() - { - $map = new WeakKeyMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $obj2 = new stdClass(); - - $storage->attach($obj1); - - $this->assertCount(1, $storage); - - $map->attach($obj1); - $map->attach($obj2); - $this->assertCount(2, $map); - - $map->removeAll($storage); - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - } - - public function testRemoveAllExcept() - { - $map = new WeakKeyMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $obj2 = new stdClass(); - - $storage->attach($obj1); - - $this->assertCount(1, $storage); - - $map->attach($obj1); - $map->attach($obj2); - $this->assertCount(2, $map); - - $map->removeAllExcept($storage); - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertFalse($map->contains($obj2)); - } -} diff --git a/tests/WeakKeyValueMapTest.php b/tests/WeakKeyValueMapTest.php deleted file mode 100644 index e195cd4..0000000 --- a/tests/WeakKeyValueMapTest.php +++ /dev/null @@ -1,436 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref\Tests; - -use PHPUnit_Framework_TestCase; -use Ref\NotifierException; -use Ref\WeakKeyValueMap; -use SplObjectStorage; -use stdClass; - -class WeakKeyValueMapTest extends PHPUnit_Framework_TestCase -{ - public function testAttach() - { - $map = new WeakKeyValueMap(); - - $obj = new stdClass(); - $inf = new stdClass(); - $map->attach($obj, $inf); - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj)); - $this->assertTrue(isset($map[$obj])); - $this->assertSame($inf, $map[$obj]); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage WeakKeyValueMap expects data to be object, NULL given - */ - public function testAttachNoData() - { - $map = new WeakKeyValueMap(); - - $obj = new stdClass(); - $map->attach($obj); - } - - public function testInfoRemoved() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1 = null; - - $this->assertCount(1, $map); - - $this->assertFalse($map->contains($obj1)); - $this->assertFalse(isset($map[$obj1])); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - } - - public function testDataRemoved() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $obj1 = null; - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - } - - public function testGetInfo() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - $map->rewind(); - $this->assertSame($inf1, $map->getInfo()); - - $map->next(); - $this->assertSame($inf2, $map->getInfo()); - } - - public function testSetInfo() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = new stdClass(); - - $map->rewind(); - $map->setInfo($inf1_new); - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1_new, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = null; - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - } - - public function testSetInfoWithNoCurrent() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $this->assertCount(1, $map); - - $inf1_new = new stdClass(); - - $map->setInfo($inf1_new); - - $this->assertCount(1, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - } - - public function testSetInfoNotObjectOnNonexistentCurrent() - { - $map = new WeakKeyValueMap(); - $map->setInfo(null); - - $this->assertTrue(true); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage WeakKeyValueMap expects data to be object, NULL given - */ - public function testSetInfoNotObject() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $map->rewind(); - $map->setInfo(null); - } - - public function testOffsetSet() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map[$obj1] = $inf1; - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map[$obj2] = $inf2; - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = new stdClass(); - - $map[$obj1] = $inf1_new; - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1_new, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = null; - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage WeakKeyValueMap expects data to be object, string given - */ - public function testOffsetSetWhenNotObject() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - - $map[$obj1] = 'should fail'; - } - - public function testGetCurrent() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - $map->rewind(); - $this->assertSame($obj1, $map->current()); - $this->assertSame($inf1, $map->getInfo()); - $map->next(); - $this->assertSame($obj2, $map->current()); - $this->assertSame($inf2, $map->getInfo()); - } - - public function testOffsetSetAttaches() - { - $map = new WeakKeyValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map[$obj1] = $inf1; - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - } - - public function testItemRemovedEvenWhenExceptionThrownInDtor() - { - $map = new WeakKeyValueMap(); - - $obj1 = new class - { - function __destruct() - { - throw new \Exception('Prevent weakref handler to be executed'); - } - }; - - $inf1 = new stdClass(); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - try { - $obj1 = null; - } catch (NotifierException $e) { - $this->assertSame('One or more exceptions thrown during notifiers calling', $e->getMessage()); - $this->assertSame('Prevent weakref handler to be executed', $e->getExceptions()[0]->getMessage()); - } - - $this->assertCount(1, $map); - $map->rewind(); - $this->assertSame($obj2, $map->current()); - $this->assertSame($inf2, $map->getInfo()); - } - - public function testAddAll() - { - $map = new WeakKeyValueMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $storage->attach($obj1, $inf1); - $storage->attach($obj2, $inf2); - - $this->assertCount(2, $storage); - - $map->addAll($storage); - - $this->assertCount(2, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - - $storage = null; - $inf1 = null; - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - - $obj2 = null; - - $this->assertCount(0, $map); - } - - public function testRemoveAll() - { - $map = new WeakKeyValueMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $storage->attach($obj1); - - $this->assertCount(1, $storage); - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - $this->assertCount(2, $map); - - $map->removeAll($storage); - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - } - - public function testRemoveAllExcept() - { - $map = new WeakKeyValueMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $storage->attach($obj1); - - $this->assertCount(1, $storage); - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - $this->assertCount(2, $map); - - $map->removeAllExcept($storage); - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertFalse($map->contains($obj2)); - } -} diff --git a/tests/WeakValueMapTest.php b/tests/WeakValueMapTest.php deleted file mode 100644 index 0399c24..0000000 --- a/tests/WeakValueMapTest.php +++ /dev/null @@ -1,374 +0,0 @@ - - * - * Licensed under the MIT license: http://opensource.org/licenses/MIT - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code or visit http://opensource.org/licenses/MIT - */ - -namespace Ref\Tests; - -use PHPUnit_Framework_TestCase; -use Ref\WeakValueMap; -use SplObjectStorage; -use stdClass; - -class WeakValueMapTest extends PHPUnit_Framework_TestCase -{ - public function testAttach() - { - $map = new WeakValueMap(); - - $obj = new stdClass(); - $inf = new stdClass(); - $map->attach($obj, $inf); - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj)); - $this->assertTrue(isset($map[$obj])); - $this->assertSame($inf, $map[$obj]); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage WeakValueMap expects data to be object, NULL given - */ - public function testAttachNoData() - { - $map = new WeakValueMap(); - - $obj = new stdClass(); - $map->attach($obj); - } - - public function testInfoRemoved() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1 = null; - - $this->assertCount(1, $map); - - $this->assertFalse($map->contains($obj1)); - $this->assertFalse(isset($map[$obj1])); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - } - - public function testGetInfo() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - $map->rewind(); - $this->assertSame($inf1, $map->getInfo()); - - $map->next(); - $this->assertSame($inf2, $map->getInfo()); - } - - public function testSetInfo() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = new stdClass(); - - $map->rewind(); - $map->setInfo($inf1_new); - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1_new, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = null; - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - } - - public function testSetInfoWithNoCurrent() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $this->assertCount(1, $map); - - $inf1_new = new stdClass(); - - $map->setInfo($inf1_new); - - $this->assertCount(1, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - } - - public function testSetInfoNotObjectOnNonexistentCurrent() - { - $map = new WeakValueMap(); - $map->setInfo(null); - - $this->assertTrue(true); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage WeakValueMap expects data to be object, NULL given - */ - public function testSetInfoNotObject() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $map->rewind(); - $map->setInfo(null); - } - - public function testOffsetSet() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map[$obj1] = $inf1; - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map[$obj2] = $inf2; - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = new stdClass(); - $map[$obj1] = $inf1_new; - - $this->assertCount(2, $map); - - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1_new, $map[$obj1]); - - $this->assertTrue($map->contains($obj2)); - $this->assertTrue(isset($map[$obj2])); - $this->assertSame($inf2, $map[$obj2]); - - $inf1_new = null; - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - } - - public function testOffsetUnset() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map->attach($obj1, $inf1); - - $obj2 = new stdClass(); - $inf2 = new stdClass(); - $map->attach($obj2, $inf2); - - $this->assertCount(2, $map); - - unset($map[$obj1]); - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj2)); - - unset($map[$obj2]); - $this->assertCount(0, $map); - } - - /** - * @expectedException \RuntimeException - * @expectedExceptionMessage WeakValueMap expects data to be object, string given - */ - public function testOffsetSetWhenNotObject() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - - $map[$obj1] = 'should fail'; - } - - public function testOffsetSetAttaches() - { - $map = new WeakValueMap(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $map[$obj1] = $inf1; - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertTrue(isset($map[$obj1])); - $this->assertSame($inf1, $map[$obj1]); - } - - public function testAddAll() - { - $map = new WeakValueMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $storage->attach($obj1, $inf1); - $storage->attach($obj2, $inf2); - - $this->assertCount(2, $storage); - - $map->addAll($storage); - - $this->assertCount(2, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - - $storage = null; - - $inf1 = null; - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - - $inf2 = null; - $this->assertCount(0, $map); - - } - - public function testRemoveAll() - { - $map = new WeakValueMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $storage->attach($obj1); - - $this->assertCount(1, $storage); - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - $this->assertCount(2, $map); - - $map->removeAll($storage); - - $this->assertCount(1, $map); - $this->assertFalse($map->contains($obj1)); - $this->assertTrue($map->contains($obj2)); - - $inf2 = null; - $this->assertCount(0, $map); - } - - public function testRemoveAllExcept() - { - $map = new WeakValueMap(); - $storage = new SplObjectStorage(); - - $obj1 = new stdClass(); - $inf1 = new stdClass(); - $obj2 = new stdClass(); - $inf2 = new stdClass(); - - $storage->attach($obj1); - - $this->assertCount(1, $storage); - - $map->attach($obj1, $inf1); - $map->attach($obj2, $inf2); - $this->assertCount(2, $map); - - $map->removeAllExcept($storage); - - $this->assertCount(1, $map); - $this->assertTrue($map->contains($obj1)); - $this->assertFalse($map->contains($obj2)); - - $inf1 = null; - - $this->assertCount(0, $map); - } -} diff --git a/tests/bootstrap.php b/tests/bootstrap.php index 98f66af..d1b86e0 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -1,9 +1,9 @@ + * Copyright (c) 2016-2017 Bogdan Padalko * * Licensed under the MIT license: http://opensource.org/licenses/MIT *