1<?php
2
3declare(strict_types=1);
4
5namespace Doctrine\DBAL\Types;
6
7use Doctrine\DBAL\Exception;
8
9use function array_search;
10use function in_array;
11
12/**
13 * The type registry is responsible for holding a map of all known DBAL types.
14 * The types are stored using the flyweight pattern so that one type only exists as exactly one instance.
15 */
16final class TypeRegistry
17{
18    /** @var array<string, Type> Map of type names and their corresponding flyweight objects. */
19    private $instances;
20
21    /**
22     * @param array<string, Type> $instances
23     */
24    public function __construct(array $instances = [])
25    {
26        $this->instances = $instances;
27    }
28
29    /**
30     * Finds a type by the given name.
31     *
32     * @throws Exception
33     */
34    public function get(string $name): Type
35    {
36        if (! isset($this->instances[$name])) {
37            throw Exception::unknownColumnType($name);
38        }
39
40        return $this->instances[$name];
41    }
42
43    /**
44     * Finds a name for the given type.
45     *
46     * @throws Exception
47     */
48    public function lookupName(Type $type): string
49    {
50        $name = $this->findTypeName($type);
51
52        if ($name === null) {
53            throw Exception::typeNotRegistered($type);
54        }
55
56        return $name;
57    }
58
59    /**
60     * Checks if there is a type of the given name.
61     */
62    public function has(string $name): bool
63    {
64        return isset($this->instances[$name]);
65    }
66
67    /**
68     * Registers a custom type to the type map.
69     *
70     * @throws Exception
71     */
72    public function register(string $name, Type $type): void
73    {
74        if (isset($this->instances[$name])) {
75            throw Exception::typeExists($name);
76        }
77
78        if ($this->findTypeName($type) !== null) {
79            throw Exception::typeAlreadyRegistered($type);
80        }
81
82        $this->instances[$name] = $type;
83    }
84
85    /**
86     * Overrides an already defined type to use a different implementation.
87     *
88     * @throws Exception
89     */
90    public function override(string $name, Type $type): void
91    {
92        if (! isset($this->instances[$name])) {
93            throw Exception::typeNotFound($name);
94        }
95
96        if (! in_array($this->findTypeName($type), [$name, null], true)) {
97            throw Exception::typeAlreadyRegistered($type);
98        }
99
100        $this->instances[$name] = $type;
101    }
102
103    /**
104     * Gets the map of all registered types and their corresponding type instances.
105     *
106     * @internal
107     *
108     * @return array<string, Type>
109     */
110    public function getMap(): array
111    {
112        return $this->instances;
113    }
114
115    private function findTypeName(Type $type): ?string
116    {
117        $name = array_search($type, $this->instances, true);
118
119        if ($name === false) {
120            return null;
121        }
122
123        return $name;
124    }
125}
126