diff --git a/.scrutinizer.yml b/.scrutinizer.yml new file mode 100644 index 0000000..87fda20 --- /dev/null +++ b/.scrutinizer.yml @@ -0,0 +1,2 @@ +tools: + external_code_coverage: true diff --git a/.travis.yml b/.travis.yml index d17bbe1..2a3fad3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -27,4 +27,8 @@ before_script: - composer install script: - - vendor/bin/phpunit --coverage-text --configuration phpunit.xml + - vendor/bin/phpunit --coverage-text --configuration phpunit.xml --coverage-clover=coverage.clover + +after_script: + - wget https://scrutinizer-ci.com/ocular.phar + - php ocular.phar code-coverage:upload --format=php-clover coverage.clover diff --git a/README.md b/README.md index 8776b36..a65b9df 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,8 @@ -# Weak-referenced data structures for PHP [![Build Status](https://travis-ci.org/pinepain/php-weak-lib.svg)](https://travis-ci.org/pinepain/php-weak-lib) +# Weak-referenced data structures for PHP + +[![Build Status](https://travis-ci.org/pinepain/php-weak-lib.svg)](https://travis-ci.org/pinepain/php-weak-lib) +[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/pinepain/php-weak-lib/badges/quality-score.png)](https://scrutinizer-ci.com/g/pinepain/php-weak-lib) +[![Code Coverage](https://scrutinizer-ci.com/g/pinepain/php-weak-lib/badges/coverage.png)](https://scrutinizer-ci.com/g/pinepain/php-weak-lib) This library is based on [php-weak][php-weak-ext] PHP extension and provides various weak data structures: diff --git a/src/AbstractWeakMap.php b/src/AbstractWeakMap.php new file mode 100644 index 0000000..383079b --- /dev/null +++ b/src/AbstractWeakMap.php @@ -0,0 +1,169 @@ + + * + * 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 Weak; + +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 Reference) { + $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 Reference($data, function () use ($object) { + $this->detach($object); + }); + + return $data; + } +} diff --git a/src/WeakKeyMap.php b/src/WeakKeyMap.php index ad0a35a..1cfa399 100644 --- a/src/WeakKeyMap.php +++ b/src/WeakKeyMap.php @@ -13,69 +13,7 @@ namespace Weak; -use SplObjectStorage; - -class WeakKeyMap extends SplObjectStorage +class WeakKeyMap extends AbstractWeakMap { - /** {@inheritdoc} */ - public function attach($object, $data = null) - { - $object = new HashedReference($object, [$this, 'detach'], 'spl_object_hash'); - - parent::attach($object, $data); - } - - /** {@inheritdoc} */ - public function current() - { - $current = parent::current(); - - if ($current instanceof Reference) { - $current = $current->get(); - } - - /* In some rare cases (it should never happens normally) orphaned weak reference may occurs in storage, so - * so $current here MAY be null - */ - - return $current; - } - - /** {@inheritdoc} */ - public function offsetSet($object, $data = null) - { - $this->attach($object, $data); - } - - /** {@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 ($object instanceof HashedReference) { - return $object->getHash(); - } - - return parent::getHash($object); - } + protected $behavior = self::WEAK_KEY; } diff --git a/src/WeakKeyValueMap.php b/src/WeakKeyValueMap.php index 70a2444..da0a9dc 100644 --- a/src/WeakKeyValueMap.php +++ b/src/WeakKeyValueMap.php @@ -13,124 +13,7 @@ namespace Weak; -use RuntimeException; -use SplObjectStorage; - -class WeakKeyValueMap extends SplObjectStorage +class WeakKeyValueMap extends AbstractWeakMap { - /** {@inheritdoc} */ - public function attach($object, $data = null) - { - $this->validateInfo($data); - - $object = new HashedReference($object, [$this, 'detach'], 'spl_object_hash'); - - $data = new Reference($data, function () use ($object) { - $this->detach($object); - }); - - parent::attach($object, $data); - } - - /** {@inheritdoc} */ - public function current() - { - $current = parent::current(); - - if ($current instanceof Reference) { - $current = $current->get(); - } - - /* In some rare cases (it should never happens normally) orphaned weak reference may occurs in storage, so - * so $current here MAY be null - */ - - return $current; - } - - /** {@inheritdoc} */ - public function addAll($storage) - { - foreach ($storage as $obj) { - $this->attach($obj, $storage[$obj]); - } - } - - 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 ($object instanceof HashedReference) { - return $object->getHash(); - } - - return parent::getHash($object); - } - - /** {@inheritdoc} */ - public function getInfo() - { - $info = parent::getInfo(); // TODO: Change the autogenerated stub - - if ($info instanceof Reference) { - $info = $info->get(); - } - - return $info; - } - - /** {@inheritdoc} */ - public function setInfo($data) - { - $this->validateInfo($data); - - $object = $this->current(); - - if (!$object) { - return; // nothing to do - } - - $data = new Reference($data, function () use ($object) { - $this->detach($object); - }); - - parent::setInfo($data); - } - - /** {@inheritdoc} */ - public function offsetSet($object, $data = null) - { - $this->attach($object, $data); - } - - /** {@inheritdoc} */ - public function offsetGet($object) - { - $info = parent::offsetGet($object); // TODO: Change the autogenerated stub - - if ($info instanceof Reference) { - $info = $info->get(); - } - - return $info; - } - - protected function validateInfo($data) - { - if (!is_object($data)) { - throw new RuntimeException(self::class . ' expects data to be object, ' . gettype($data) . ' given'); - } - } + protected $behavior = self::WEAK_KEY | self::WEAK_VAL; } diff --git a/src/WeakValueMap.php b/src/WeakValueMap.php index 25c070b..17c72e1 100644 --- a/src/WeakValueMap.php +++ b/src/WeakValueMap.php @@ -13,83 +13,7 @@ namespace Weak; -use RuntimeException; -use SplObjectStorage; - -class WeakValueMap extends SplObjectStorage +class WeakValueMap extends AbstractWeakMap { - /** {@inheritdoc} */ - public function attach($object, $data = null) - { - $this->validateInfo($data); - - $data = new Reference($data, function () use ($object) { - $this->detach($object); - }); - - parent::attach($object, $data); - } - - /** {@inheritdoc} */ - public function addAll($storage) - { - foreach ($storage as $obj) { - $this->attach($obj, $storage[$obj]); - } - } - - /** {@inheritdoc} */ - public function getInfo() - { - $info = parent::getInfo(); // TODO: Change the autogenerated stub - - if ($info instanceof Reference) { - $info = $info->get(); - } - - return $info; - } - - /** {@inheritdoc} */ - public function setInfo($data) - { - $this->validateInfo($data); - - $object = $this->current(); - - if (!$object) { - return; // nothing to do - } - - $data = new Reference($data, function () use ($object) { - $this->detach($object); - }); - - parent::setInfo($data); - } - - /** {@inheritdoc} */ - public function offsetSet($object, $data = null) - { - $this->attach($object, $data); - } - - /** {@inheritdoc} */ - public function offsetGet($object) - { - $info = parent::offsetGet($object); // TODO: Change the autogenerated stub - - if ($info instanceof Reference) { - $info = $info->get(); - } - - return $info; - } - - protected function validateInfo($data) - { - if (!is_object($data)) { - throw new RuntimeException(self::class . ' expects data to be object, ' . gettype($data) . ' given'); - } - } + protected $behavior = self::WEAK_VAL; } diff --git a/tests/WeakKeyValueMapTest.php b/tests/WeakKeyValueMapTest.php index 7f51d18..451f404 100644 --- a/tests/WeakKeyValueMapTest.php +++ b/tests/WeakKeyValueMapTest.php @@ -196,6 +196,15 @@ public function testSetInfoWithNoCurrent() $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 @@ -203,6 +212,12 @@ public function testSetInfoWithNoCurrent() public function testSetInfoNotObject() { $map = new WeakKeyValueMap(); + + $obj1 = new stdClass(); + $inf1 = new stdClass(); + $map->attach($obj1, $inf1); + + $map->rewind(); $map->setInfo(null); } diff --git a/tests/WeakValueMapTest.php b/tests/WeakValueMapTest.php index 69361f3..31e9733 100644 --- a/tests/WeakValueMapTest.php +++ b/tests/WeakValueMapTest.php @@ -166,6 +166,15 @@ public function testSetInfoWithNoCurrent() $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 @@ -173,6 +182,12 @@ public function testSetInfoWithNoCurrent() public function testSetInfoNotObject() { $map = new WeakValueMap(); + + $obj1 = new stdClass(); + $inf1 = new stdClass(); + $map->attach($obj1, $inf1); + + $map->rewind(); $map->setInfo(null); }