Skip to content

Commit

Permalink
Extract type factory and registry from Type into TypeRegistry
Browse files Browse the repository at this point in the history
  • Loading branch information
Majkl578 committed Apr 13, 2019
1 parent 773275d commit 2db20f2
Show file tree
Hide file tree
Showing 5 changed files with 398 additions and 73 deletions.
14 changes: 14 additions & 0 deletions lib/Doctrine/DBAL/DBALException.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Doctrine\DBAL\Driver\ExceptionConverterDriver;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use Exception;
use Throwable;
use function array_map;
Expand All @@ -18,6 +19,7 @@
use function is_string;
use function json_encode;
use function preg_replace;
use function spl_object_hash;
use function sprintf;

class DBALException extends Exception
Expand Down Expand Up @@ -277,4 +279,16 @@ public static function typeNotFound($name)
{
return new self('Type to be overwritten ' . $name . ' does not exist.');
}

public static function typeNotRegistered(Type $type) : self
{
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))
);
}
}
151 changes: 78 additions & 73 deletions lib/Doctrine/DBAL/Types/Type.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Doctrine\DBAL\DBALException;
use Doctrine\DBAL\ParameterType;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use function array_map;
use function get_class;
use function str_replace;
use function strrpos;
use function substr;
Expand All @@ -16,76 +18,70 @@
*/
abstract class Type
{
public const TARRAY = 'array';
public const SIMPLE_ARRAY = 'simple_array';
public const JSON_ARRAY = 'json_array';
public const JSON = 'json';
public const BIGINT = 'bigint';
public const BINARY = 'binary';
public const BLOB = 'blob';
public const BOOLEAN = 'boolean';
public const DATE = 'date';
public const DATE_IMMUTABLE = 'date_immutable';
public const DATEINTERVAL = 'dateinterval';
public const DATETIME = 'datetime';
public const DATETIME_IMMUTABLE = 'datetime_immutable';
public const DATETIMETZ = 'datetimetz';
public const DATETIMETZ_IMMUTABLE = 'datetimetz_immutable';
public const DATE = 'date';
public const DATE_IMMUTABLE = 'date_immutable';
public const TIME = 'time';
public const TIME_IMMUTABLE = 'time_immutable';
public const DECIMAL = 'decimal';
public const FLOAT = 'float';
public const GUID = 'guid';
public const INTEGER = 'integer';
public const JSON = 'json';
public const JSON_ARRAY = 'json_array';
public const OBJECT = 'object';
public const SIMPLE_ARRAY = 'simple_array';
public const SMALLINT = 'smallint';
public const STRING = 'string';
public const TARRAY = 'array';
public const TEXT = 'text';
public const BINARY = 'binary';
public const BLOB = 'blob';
public const FLOAT = 'float';
public const GUID = 'guid';
public const DATEINTERVAL = 'dateinterval';

/**
* Map of already instantiated type objects. One instance per type (flyweight).
*
* @var self[]
*/
private static $_typeObjects = [];
public const TIME = 'time';
public const TIME_IMMUTABLE = 'time_immutable';

/**
* The map of supported doctrine mapping types.
*
* @var string[]
*/
private static $_typesMap = [
self::TARRAY => ArrayType::class,
self::SIMPLE_ARRAY => SimpleArrayType::class,
self::JSON_ARRAY => JsonArrayType::class,
self::JSON => JsonType::class,
self::OBJECT => ObjectType::class,
self::BOOLEAN => BooleanType::class,
self::INTEGER => IntegerType::class,
self::SMALLINT => SmallIntType::class,
self::BIGINT => BigIntType::class,
self::STRING => StringType::class,
self::TEXT => TextType::class,
self::DATETIME => DateTimeType::class,
self::DATETIME_IMMUTABLE => DateTimeImmutableType::class,
self::DATETIMETZ => DateTimeTzType::class,
private const BUILTIN_TYPES_MAP = [
self::BIGINT => BigIntType::class,
self::BINARY => BinaryType::class,
self::BLOB => BlobType::class,
self::BOOLEAN => BooleanType::class,
self::DATE => DateType::class,
self::DATE_IMMUTABLE => DateImmutableType::class,
self::DATEINTERVAL => DateIntervalType::class,
self::DATETIME => DateTimeType::class,
self::DATETIME_IMMUTABLE => DateTimeImmutableType::class,
self::DATETIMETZ => DateTimeTzType::class,
self::DATETIMETZ_IMMUTABLE => DateTimeTzImmutableType::class,
self::DATE => DateType::class,
self::DATE_IMMUTABLE => DateImmutableType::class,
self::TIME => TimeType::class,
self::TIME_IMMUTABLE => TimeImmutableType::class,
self::DECIMAL => DecimalType::class,
self::FLOAT => FloatType::class,
self::BINARY => BinaryType::class,
self::BLOB => BlobType::class,
self::GUID => GuidType::class,
self::DATEINTERVAL => DateIntervalType::class,
self::DECIMAL => DecimalType::class,
self::FLOAT => FloatType::class,
self::GUID => GuidType::class,
self::INTEGER => IntegerType::class,
self::JSON => JsonType::class,
self::JSON_ARRAY => JsonArrayType::class,
self::OBJECT => ObjectType::class,
self::SIMPLE_ARRAY => SimpleArrayType::class,
self::SMALLINT => SmallIntType::class,
self::STRING => StringType::class,
self::TARRAY => ArrayType::class,
self::TEXT => TextType::class,
self::TIME => TimeType::class,
self::TIME_IMMUTABLE => TimeImmutableType::class,
];

/** @var TypeRegistry|null */
private static $typeRegistry;

/**
* Prevents instantiation and forces use of the factory method.
* @internal Do not instantiate directly - use {@see Type::addType()} method instead.
*/
final private function __construct()
final public function __construct()
{
}

Expand Down Expand Up @@ -148,6 +144,29 @@ abstract public function getSQLDeclaration(array $fieldDeclaration, AbstractPlat
*/
abstract public function getName();

/**
* @internal This method is only to be used within DBAL for forward compatibility purposes. Do not use directly.
*/
final public static function getTypeRegistry() : TypeRegistry
{
if (self::$typeRegistry === null) {
self::$typeRegistry = self::createTypeRegistry();
}

return self::$typeRegistry;
}

private static function createTypeRegistry() : TypeRegistry
{
$registry = new TypeRegistry();

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

return $registry;
}

/**
* Factory method to create type instances.
* Type instances are implemented as flyweights.
Expand All @@ -160,14 +179,7 @@ abstract public function getName();
*/
public static function getType($name)
{
if (! isset(self::$_typeObjects[$name])) {
if (! isset(self::$_typesMap[$name])) {
throw DBALException::unknownColumnType($name);
}
self::$_typeObjects[$name] = new self::$_typesMap[$name]();
}

return self::$_typeObjects[$name];
return self::getTypeRegistry()->get($name);
}

/**
Expand All @@ -182,11 +194,7 @@ public static function getType($name)
*/
public static function addType($name, $className)
{
if (isset(self::$_typesMap[$name])) {
throw DBALException::typeExists($name);
}

self::$_typesMap[$name] = $className;
self::getTypeRegistry()->register($name, new $className());
}

/**
Expand All @@ -198,7 +206,7 @@ public static function addType($name, $className)
*/
public static function hasType($name)
{
return isset(self::$_typesMap[$name]);
return self::getTypeRegistry()->has($name);
}

/**
Expand All @@ -213,15 +221,7 @@ public static function hasType($name)
*/
public static function overrideType($name, $className)
{
if (! isset(self::$_typesMap[$name])) {
throw DBALException::typeNotFound($name);
}

if (isset(self::$_typeObjects[$name])) {
unset(self::$_typeObjects[$name]);
}

self::$_typesMap[$name] = $className;
self::getTypeRegistry()->override($name, new $className());
}

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

/**
Expand Down
118 changes: 118 additions & 0 deletions lib/Doctrine/DBAL/Types/TypeRegistry.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
<?php

declare(strict_types=1);

namespace Doctrine\DBAL\Types;

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

/**
* Type registry is responsible for holding holding a map of known DBAL types.
* Type instances are implemented as flyweight pattern.
*
* @internal TypeRegistry class exists for forward compatibility, its API should not be considered stable.
*/
final class TypeRegistry
{
/** @var array<string, Type> Map of type names and their corresponding flyweight objects. */
private $instances = [];

/**
* Finds a type by the given name.
*
* @throws DBALException
*/
public function get(string $name) : Type
{
if (! isset($this->instances[$name])) {
throw DBALException::unknownColumnType($name);
}

return $this->instances[$name];
}

/**
* Finds a name for the given type.
*
* @throws DBALException
*/
public function lookupName(Type $type) : string
{
$name = $this->findTypeName($type);

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

return $name;
}

/**
* Checks if there is a type of the given name.
*/
public function has(string $name) : bool
{
return isset($this->instances[$name]);
}

/**
* Registers a custom type to the type map.
*
* @throws DBALException
*/
public function register(string $name, Type $type) : void
{
if (isset($this->instances[$name])) {
throw DBALException::typeExists($name);
}

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, Type $type) : void
{
if (! isset($this->instances[$name])) {
throw DBALException::typeNotFound($name);
}

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

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

/**
* Gets the map of all registered types and their corresponding type instances.
*
* @internal
*
* @return array<string, Type>
*/
public function getMap() : array
{
return $this->instances;
}

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

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

return $name;
}
}
Loading

0 comments on commit 2db20f2

Please sign in to comment.