Skip to content

Commit

Permalink
adds Set implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
schmittjoh committed May 17, 2015
1 parent 94a2c04 commit 9a72e5b
Show file tree
Hide file tree
Showing 10 changed files with 1,017 additions and 2 deletions.
38 changes: 36 additions & 2 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ Supported Collections:
- Classes: ``Map``, ``ObjectMap`` (not yet implemented)


- Sets (not yet implemented)
- Sets

- Keys: not meaningful
- Values: anything, each value must be unique (===)
- Values: objects, or scalars, each value is guaranteed to be unique (see Set usage below for details)
- Classes: ``Set``

General Characteristics:
Expand All @@ -48,6 +48,40 @@ Usage
-----
Collection classes provide a rich API.

Sets
~~~~
In a Set each value is guaranteed to be unique. The ``Set`` class supports objects, and scalars as value. Equality
is determined via the following steps.

**Equality of Objects**

1. If an object implements ``ObjectBasics``, equality is determined by the ``equals()`` method.
2. If an object has an external handler like the ``DateTime`` that was registered via ``ObjectBasicsHandlerRegistry::registerHandlerFor``,
equality is determined by that handler's ``equals()`` method.
3. If none of the above is applicable, equality is determined by identity ``$a === $b``.

**Equality of Scalars**

Scalar are considered equal if ``$a === $b`` is true.


.. code-block :: php
$set = new Set();
$set->add(new \DateTime('today'));
$set->add(new \DateTime('today'));
var_dump(count($set)); // int(1) -> the same date is not added twice
foreach ($set as $date) {
var_dump($date);
}
$set->all();
$set->addSet($otherSet);
$set->addAll($someElements);
Sequences
~~~~~~~~~

Expand Down
23 changes: 23 additions & 0 deletions src/PhpCollection/EntityLikeObject.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?php

namespace PhpCollection;

/**
* Implementation for ObjectBasics for entity-like objects.
*
* Two objects are considered equal if they refer to the same instance.
*
* @author Johannes M. Schmitt <[email protected]>
*/
trait EntityLikeObject
{
public function hash()
{
return spl_object_hash($this);
}

public function equals(ObjectBasics $other)
{
return $this === $other;
}
}
40 changes: 40 additions & 0 deletions src/PhpCollection/ObjectBasics.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
<?php

namespace PhpCollection;

/**
* Interface that must be implemented by objects that are used as keys, or in sets.
*
* For entities, you can use the "EntityLikeObject" trait.
*
* @author Johannes M. Schmitt <[email protected]>
*/
interface ObjectBasics
{
/**
* Produces a hash for the given object.
*
* If two objects are equal (as per the equals() method), the hash() method must produce
* the same hash for them.
*
* The reverse can, but does not necessarily have to be true. That is, if two objects have the
* same hash, they do not necessarily have to be equal, but the equals() method must be called
* to be sure.
*
* When implementing this method try to use a simple and fast algorithm that produces reasonably
* different results for non-equal objects, and shift the heavy comparison logic to equals().
*
* @return string|integer
*/
public function hash();

/**
* Whether two objects are equal.
*
* This can compare by referential equality (===), or in case of value objects like (\DateTime) compare
* the individual properties of the objects; it's up to the implementation.
*
* @return boolean
*/
public function equals(ObjectBasics $other);
}
24 changes: 24 additions & 0 deletions src/PhpCollection/ObjectBasicsHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php

namespace PhpCollection;

/**
* Interface for external handlers that provide ObjectBasics functionality.
*
* @author Johannes M. Schmitt <[email protected]>
*/
interface ObjectBasicsHandler
{
/**
* @param object $object This object is guaranteed to be of the type the handler was registered for.
* @return string|integer
*/
public function hash($object);

/**
* @param object $firstObject This object is guaranteed to be of the type the handler was registered for.
* @param object $secondObject This might be an object of any class.
* @return boolean
*/
public function equals($firstObject, $secondObject);
}
29 changes: 29 additions & 0 deletions src/PhpCollection/ObjectBasicsHandler/DateTimeHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

namespace PhpCollection\ObjectBasicsHandler;

use PhpCollection\ObjectBasicsHandler;

class DateTimeHandler implements ObjectBasicsHandler
{
public function hash($object)
{
if ( ! $object instanceof \DateTime) {
throw new \LogicException('$object must be an instance of \DateTime.');
}

return $object->getTimestamp();
}

public function equals($thisObject, $otherObject)
{
if ( ! $thisObject instanceof \DateTime) {
throw new \LogicException('$thisObject must be an instance of \DateTime.');
}
if ( ! $otherObject instanceof \DateTime) {
return false;
}

return $thisObject->format(\DateTime::ISO8601) === $otherObject->format(\DateTime::ISO8601);
}
}
18 changes: 18 additions & 0 deletions src/PhpCollection/ObjectBasicsHandler/IdentityHandler.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

namespace PhpCollection\ObjectBasicsHandler;

use PhpCollection\ObjectBasicsHandler;

class IdentityHandler implements ObjectBasicsHandler
{
public function hash($object)
{
return spl_object_hash($object);
}

public function equals($a, $b)
{
return $a === $b;
}
}
82 changes: 82 additions & 0 deletions src/PhpCollection/ObjectBasicsHandlerRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
<?php

namespace PhpCollection;
use PhpCollection\ObjectBasicsHandler\IdentityHandler;

/**
* Registry for handlers that provide ObjectBasics functionality for classes.
*
* You want to register a handler if you cannot implement the ObjectBasics interface, for example
* because a class is provided by a third-party package, or built into PHP.
*
* @author Johannes M. Schmitt <[email protected]>
*/
abstract class ObjectBasicsHandlerRegistry
{
private static $handlers = array(
'DateTime' => 'PhpCollection\\ObjectBasicsHandler\\DateTimeHandler',
);
private static $defaultObjectHandler;

private static $aliases = array();

/**
* Defines an alias.
*
* $aliasClass must be a sub-type (extend or implement) $handlingClass; otherwise you will run into trouble.
*
* Aliases can only be one level deep,
*
* i.e. aliasClass -> handlingClass is supported,
* but aliasClass -> anotherAliasClass -> handlingClass is not.
*
* @param string $handlingClass The class that should be aliased, i.e. MyDateTime
* @param string $aliasClass The class that should be used instead, i.e. DateTime
*/
public static function addAliasFor($handlingClass, $aliasClass)
{
self::$aliases[$handlingClass] = $aliasClass;
}

public static function addHandlerFor($handlingClass, $handlerInstanceOrClassName)
{
if ( ! $handlerInstanceOrClassName instanceof ObjectBasicsHandler && ! is_string($handlerInstanceOrClassName)) {
throw new \LogicException('$handler must be an instance of ObjectBasicsHandler, or a string referring to the handlers class.');
}

self::$handlers[$handlingClass] = $handlerInstanceOrClassName;
}

public static function getHandler($className)
{
if (isset(self::$aliases[$className])) {
$className = self::$aliases[$className];
}

if ( ! isset(self::$handlers[$className])) {
if (self::$defaultObjectHandler === null) {
self::$defaultObjectHandler = new IdentityHandler();
}

return self::$defaultObjectHandler;
}

if (self::$handlers[$className] instanceof ObjectBasicsHandler) {
return self::$handlers[$className];
}

if (is_string(self::$handlers[$className])) {
$handlerClass = self::$handlers[$className];

return self::$handlers[$className] = new $handlerClass();
}

throw new \LogicException(sprintf(
'Unknown handler type ("%s") for class "%s" - should never be reached.',
gettype(self::$handlers[$className]),
$className
));
}

private final function __construct() { }
}
Loading

0 comments on commit 9a72e5b

Please sign in to comment.