Skip to content

Commit

Permalink
TypeRegistry accepts instances directly
Browse files Browse the repository at this point in the history
  • Loading branch information
Majkl578 committed Nov 24, 2018
1 parent 02a475f commit f9a1dfb
Show file tree
Hide file tree
Showing 4 changed files with 138 additions and 61 deletions.
11 changes: 9 additions & 2 deletions lib/Doctrine/DBAL/DBALException.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
use function is_resource;
use function is_string;
use function json_encode;
use function spl_object_id;
use function spl_object_hash;
use function sprintf;
use function str_split;

Expand Down Expand Up @@ -287,6 +287,13 @@ public static function typeNotFound($name)

public static function typeNotRegistered(Type $type) : self
{
return new self(sprintf('Type of the class %s@%d is not registered.', get_class($type), spl_object_id($type)));
return new self(sprintf('Type of the class %s@%s is not registered.', get_class($type), spl_object_hash($type)));
}

public static function typeAlreadyRegistered(Type $type) : self
{
return new self(
sprintf('Type of the class %s@%s is already registered.', get_class($type), spl_object_hash($type))
);
}
}
17 changes: 12 additions & 5 deletions lib/Doctrine/DBAL/Types/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_map;
use function end;
use function explode;
use function get_class;
use function str_replace;

/**
Expand Down Expand Up @@ -77,7 +79,7 @@ abstract class Type
private static $typeRegistry;

/**
* @internal Do not instantiate directly - use the factory method instead.
* @internal Do not instantiate directly - use {@see Type::addType()} method instead.
*/
final public function __construct()
{
Expand Down Expand Up @@ -156,7 +158,7 @@ private static function createTypeRegistry() : TypeRegistry
$registry = new TypeRegistry();

foreach (self::BUILTIN_TYPES_MAP as $name => $class) {
$registry->register($name, $class);
$registry->register($name, new $class());
}

return $registry;
Expand Down Expand Up @@ -189,7 +191,7 @@ public static function getType($name)
*/
public static function addType($name, $className)
{
self::getTypeRegistry()->register($name, $className);
self::getTypeRegistry()->register($name, new $className());
}

/**
Expand All @@ -216,7 +218,7 @@ public static function hasType($name)
*/
public static function overrideType($name, $className)
{
self::getTypeRegistry()->override($name, $className);
self::getTypeRegistry()->override($name, new $className());
}

/**
Expand All @@ -240,7 +242,12 @@ public function getBindingType()
*/
public static function getTypesMap()
{
return self::getTypeRegistry()->getTypeClasses();
return array_map(
static function (Type $type) : string {
return get_class($type);
},
self::getTypeRegistry()->getMap()
);
}

/**
Expand Down
67 changes: 37 additions & 30 deletions lib/Doctrine/DBAL/Types/TypeRegistry.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,15 @@

use Doctrine\DBAL\DBALException;
use function array_search;
use function in_array;

/**
* @internal
*/
final class TypeRegistry
{
/** @var Type[] Map of already instantiated type objects. One instance per type (flyweight). */
private $typeInstances = [];

/** @var string[] Map of defined types and their classes. */
private $typeClasses = [];
/** @var Type[] Map of type names and their corresponding objects. */
private $instances = [];

/**
* Factory method to create type instances.
Expand All @@ -26,15 +24,11 @@ final class TypeRegistry
*/
public function get(string $name) : Type
{
if (isset($this->typeInstances[$name])) {
return $this->typeInstances[$name];
}

if (isset($this->typeClasses[$name])) {
return $this->typeInstances[$name] = new $this->typeClasses[$name]();
if (! isset($this->instances[$name])) {
throw DBALException::unknownColumnType($name);
}

throw DBALException::unknownColumnType($name);
return $this->instances[$name];
}

/**
Expand All @@ -44,9 +38,9 @@ public function get(string $name) : Type
*/
public function lookupName(Type $type) : string
{
$name = array_search($type, $this->typeInstances, true);
$name = $this->findTypeName($type);

if ($name === false) {
if ($name === null) {
throw DBALException::typeNotRegistered($type);
}

Expand All @@ -58,50 +52,63 @@ public function lookupName(Type $type) : string
*/
public function has(string $name) : bool
{
return isset($this->typeClasses[$name]);
return isset($this->instances[$name]);
}

/**
* Adds a custom type to the type map.
*
* @param string $name The name of the type. This should correspond to what getName() returns.
* @param string $class The class name of the custom type.
*
* @throws DBALException
*/
public function register(string $name, string $class) : void
public function register(string $name, Type $type) : void
{
if (isset($this->typeClasses[$name])) {
if (isset($this->instances[$name])) {
throw DBALException::typeExists($name);
}

$this->typeClasses[$name] = $class;
if ($this->findTypeName($type) !== null) {
throw DBALException::typeAlreadyRegistered($type);
}

$this->instances[$name] = $type;
}

/**
* Overrides an already defined type to use a different implementation.
*
* @throws DBALException
*/
public function override(string $name, string $class) : void
public function override(string $name, Type $type) : void
{
if (! isset($this->typeClasses[$name])) {
if (! isset($this->instances[$name])) {
throw DBALException::typeNotFound($name);
}

unset($this->typeInstances[$name]);
if (! in_array($this->findTypeName($type), [$name, null], true)) {
throw DBALException::typeAlreadyRegistered($type);
}

$this->typeClasses[$name] = $class;
$this->instances[$name] = $type;
}

/**
* Gets the types array map which holds all registered types and the corresponding
* type class
* Gets the map of all registered types and their corresponding type instances.
*
* @return string[]
* @return Type[]
*/
public function getTypeClasses() : array
public function getMap() : array
{
return $this->typeClasses;
return $this->instances;
}

private function findTypeName(Type $type) : ?string
{
$name = array_search($type, $this->instances, true);

if ($name === false) {
return null;
}

return $name;
}
}
104 changes: 80 additions & 24 deletions tests/Doctrine/Tests/DBAL/Types/TypeRegistryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,32 @@

class TypeRegistryTest extends TestCase
{
private const TEST_TYPE_NAME = 'test';
private const TEST_TYPE_CLASS = BlobType::class;
private const OTHER_TYPE_NAME = 'other';
private const OTHER_TYPE_CLASS = BinaryType::class;
private const TEST_TYPE_NAME = 'test';
private const OTHER_TEST_TYPE_NAME = 'other';

/** @var TypeRegistry */
private $registry;

/** @var BlobType */
private $testType;

/** @var BinaryType */
private $otherTestType;

protected function setUp() : void
{
$this->testType = new BlobType();
$this->otherTestType = new BinaryType();

$this->registry = new TypeRegistry();
$this->registry->register(self::OTHER_TYPE_NAME, self::OTHER_TYPE_CLASS);
$this->registry->register(self::TEST_TYPE_NAME, self::TEST_TYPE_CLASS);
$this->registry->register(self::TEST_TYPE_NAME, $this->testType);
$this->registry->register(self::OTHER_TEST_TYPE_NAME, $this->otherTestType);
}

public function testGet() : void
{
self::assertInstanceOf(self::TEST_TYPE_CLASS, $this->registry->get(self::TEST_TYPE_NAME));
self::assertSame($this->testType, $this->registry->get(self::TEST_TYPE_NAME));
self::assertSame($this->otherTestType, $this->registry->get(self::OTHER_TEST_TYPE_NAME));

$this->expectException(DBALException::class);
$this->registry->get('unknown');
Expand All @@ -49,7 +57,11 @@ public function testLookupName() : void
{
self::assertSame(
self::TEST_TYPE_NAME,
$this->registry->lookupName($this->registry->get(self::TEST_TYPE_NAME))
$this->registry->lookupName($this->testType)
);
self::assertSame(
self::OTHER_TEST_TYPE_NAME,
$this->registry->lookupName($this->otherTestType)
);

$this->expectException(DBALException::class);
Expand All @@ -59,40 +71,84 @@ public function testLookupName() : void
public function testHas() : void
{
self::assertTrue($this->registry->has(self::TEST_TYPE_NAME));
self::assertTrue($this->registry->has(self::OTHER_TEST_TYPE_NAME));
self::assertFalse($this->registry->has('unknown'));
}

public function testRegister() : void
{
$this->registry->register('some', TextType::class);
$newType = new TextType();

$this->registry->register('some', $newType);

self::assertTrue($this->registry->has('some'));
self::assertInstanceOf(TextType::class, $this->registry->get('some'));
self::assertSame($newType, $this->registry->get('some'));
}

public function testRegisterWithAlradyRegisteredName() : void
{
$this->registry->register('some', new TextType());

$this->expectException(DBALException::class);
$this->registry->register('some', TextType::class);
$this->registry->register('some', new TextType());
}

public function testRegisterWithAlreadyRegisteredInstance() : void
{
$newType = new TextType();

$this->registry->register('some', $newType);

$this->expectException(DBALException::class);
$this->registry->register('other', $newType);
}

public function testOverride() : void
{
$this->registry->register('some', TextType::class);
$this->registry->override('some', StringType::class);
$baseType = new TextType();
$overrideType = new StringType();

self::assertTrue($this->registry->has('some'));
self::assertInstanceOf(StringType::class, $this->registry->get('some'));
$this->registry->register('some', $baseType);
$this->registry->override('some', $overrideType);

self::assertSame($overrideType, $this->registry->get('some'));
}

public function testOverrideAllowsExistingInstance() : void
{
$type = new TextType();

$this->registry->register('some', $type);
$this->registry->override('some', $type);

self::assertSame($type, $this->registry->get('some'));
}

public function testOverrideWithAlreadyRegisteredInstance() : void
{
$newType = new TextType();

$this->registry->register('first', $newType);
$this->registry->register('second', new StringType());

$this->expectException(DBALException::class);
$this->registry->override('unknown', StringType::class);
$this->registry->override('second', $newType);
}

public function testGetTypeClasses() : void
public function testOverrideWithUnknownType() : void
{
self::assertSame(
[
self::OTHER_TYPE_NAME => self::OTHER_TYPE_CLASS,
self::TEST_TYPE_NAME => self::TEST_TYPE_CLASS,
],
$this->registry->getTypeClasses()
);
$this->expectException(DBALException::class);
$this->registry->override('unknown', new TextType());
}

public function testGetMap() : void
{
$registeredTypes = $this->registry->getMap();

self::assertCount(2, $registeredTypes);
self::assertArrayHasKey(self::TEST_TYPE_NAME, $registeredTypes);
self::assertArrayHasKey(self::OTHER_TEST_TYPE_NAME, $registeredTypes);
self::assertSame($this->testType, $registeredTypes[self::TEST_TYPE_NAME]);
self::assertSame($this->otherTestType, $registeredTypes[self::OTHER_TEST_TYPE_NAME]);
}
}

0 comments on commit f9a1dfb

Please sign in to comment.