Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
94.29% |
33 / 35 |
|
71.43% |
5 / 7 |
CRAP | |
0.00% |
0 / 1 |
ConnectionManager | |
94.29% |
33 / 35 |
|
71.43% |
5 / 7 |
25.12 | |
0.00% |
0 / 1 |
__construct | n/a |
0 / 0 |
n/a |
0 / 0 |
1 | |||||
__clone | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__sleep | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
__wakeup | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
getInstance | |
66.67% |
2 / 3 |
|
0.00% |
0 / 1 |
2.15 | |||
getConnection | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
5 | |||
connect | |
92.31% |
12 / 13 |
|
0.00% |
0 / 1 |
4.01 | |||
parseParams | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
5 | |||
createDsn | n/a |
0 / 0 |
n/a |
0 / 0 |
5 |
1 | <?php declare(strict_types=1); |
2 | /** |
3 | * Query |
4 | * |
5 | * SQL Query Builder / Database Abstraction Layer |
6 | * |
7 | * PHP version 8.1 |
8 | * |
9 | * @package Query |
10 | * @author Timothy J. Warren <tim@timshome.page> |
11 | * @copyright 2012 - 2023 Timothy J. Warren |
12 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
13 | * @link https://git.timshomepage.net/aviat/Query |
14 | * @version 4.0.0 |
15 | */ |
16 | |
17 | namespace Query; |
18 | |
19 | use DomainException; |
20 | use stdClass; |
21 | |
22 | /** |
23 | * Connection manager class to manage connections for the |
24 | * Query method |
25 | */ |
26 | final class ConnectionManager |
27 | { |
28 | /** |
29 | * Map of named database connections |
30 | */ |
31 | private array $connections = []; |
32 | |
33 | /** |
34 | * Class instance variable |
35 | */ |
36 | private static ?ConnectionManager $instance = NULL; |
37 | |
38 | /** |
39 | * Private constructor to prevent multiple instances |
40 | * @codeCoverageIgnore |
41 | */ |
42 | private function __construct() |
43 | { |
44 | } |
45 | |
46 | /** |
47 | * Private clone method to prevent cloning |
48 | * |
49 | * @throws DomainException |
50 | * @return void |
51 | */ |
52 | public function __clone() |
53 | { |
54 | throw new DomainException("Can't clone singleton"); |
55 | } |
56 | |
57 | /** |
58 | * Prevent serialization of this object |
59 | * |
60 | * @throws DomainException |
61 | */ |
62 | public function __sleep() |
63 | { |
64 | throw new DomainException('No serializing of singleton'); |
65 | } |
66 | |
67 | /** |
68 | * Make sure serialize/deserialize doesn't work |
69 | * |
70 | * @throws DomainException |
71 | */ |
72 | public function __wakeup(): void |
73 | { |
74 | throw new DomainException("Can't unserialize singleton"); |
75 | } |
76 | |
77 | /** |
78 | * Return a connection manager instance |
79 | * |
80 | * @staticvar null $instance |
81 | */ |
82 | public static function getInstance(): ConnectionManager |
83 | { |
84 | if (self::$instance === NULL) |
85 | { |
86 | self::$instance = new self(); |
87 | } |
88 | |
89 | return self::$instance; |
90 | } |
91 | |
92 | /** |
93 | * Returns the connection specified by the name given |
94 | * |
95 | * @throws Exception\NonExistentConnectionException |
96 | */ |
97 | public function getConnection(string $name = ''): QueryBuilderInterface |
98 | { |
99 | // If the parameter is a string, use it as an array index |
100 | if (is_scalar($name) && isset($this->connections[$name])) |
101 | { |
102 | return $this->connections[$name]; |
103 | } |
104 | |
105 | if (empty($name) && ! empty($this->connections)) // Otherwise, return the last one |
106 | { |
107 | return end($this->connections); |
108 | } |
109 | |
110 | // You should actually connect before trying to get a connection... |
111 | throw new Exception\NonExistentConnectionException('The specified connection does not exist'); |
112 | } |
113 | |
114 | /** |
115 | * Parse the passed parameters and return a connection |
116 | */ |
117 | public function connect(array|object $params): QueryBuilderInterface |
118 | { |
119 | [$dsn, $dbType, $params, $options] = $this->parseParams($params); |
120 | |
121 | $dbType = ucfirst($dbType); |
122 | $driver = "\\Query\\Drivers\\{$dbType}\\Driver"; |
123 | |
124 | // Create the database connection |
125 | $db = empty($params->user) |
126 | ? new $driver($dsn, '', '', $options) |
127 | : new $driver($dsn, $params->user, $params->pass, $options); |
128 | |
129 | // Set the table prefix, if it exists |
130 | if (isset($params->prefix)) |
131 | { |
132 | $db->setTablePrefix($params->prefix); |
133 | } |
134 | |
135 | // Create Query Builder object |
136 | $conn = new QueryBuilder($db, new QueryParser($db)); |
137 | |
138 | // Save it for later |
139 | if (isset($params->alias)) |
140 | { |
141 | $this->connections[$params->alias] = $conn; |
142 | } |
143 | else |
144 | { |
145 | $this->connections[] = $conn; |
146 | } |
147 | |
148 | return $conn; |
149 | } |
150 | |
151 | /** |
152 | * Parses params into a dsn and option array |
153 | * |
154 | * @throws Exception\BadDBDriverException |
155 | */ |
156 | public function parseParams(array|object $rawParams): array |
157 | { |
158 | $params = (object) $rawParams; |
159 | $params->type = strtolower($params->type); |
160 | $dbType = ($params->type === 'postgresql') ? 'pgsql' : $params->type; |
161 | $dbType = ucfirst($dbType); |
162 | |
163 | // Make sure the class exists |
164 | if ( ! class_exists("\\Query\\Drivers\\{$dbType}\\Driver")) |
165 | { |
166 | throw new Exception\BadDBDriverException('Database driver does not exist, or is not supported'); |
167 | } |
168 | |
169 | // Set additional PDO options |
170 | $options = []; |
171 | |
172 | if (isset($params->options)) |
173 | { |
174 | $options = (array) $params->options; |
175 | } |
176 | |
177 | // Create the dsn for the database to connect to |
178 | $dsn = strtolower($dbType) === 'sqlite' ? $params->file : $this->createDsn($dbType, $params); |
179 | |
180 | return [$dsn, $dbType, $params, $options]; |
181 | } |
182 | |
183 | /** |
184 | * Create the dsn from the db type and params |
185 | * |
186 | * @codeCoverageIgnore |
187 | */ |
188 | private function createDsn(string $dbType, stdClass $params): string |
189 | { |
190 | $pairs = []; |
191 | |
192 | if ( ! empty($params->database)) |
193 | { |
194 | $pairs[] = implode('=', ['dbname', $params->database]); |
195 | } |
196 | |
197 | $skip = [ |
198 | 'name' => 'name', |
199 | 'pass' => 'pass', |
200 | 'user' => 'user', |
201 | 'type' => 'type', |
202 | 'prefix' => 'prefix', |
203 | 'options' => 'options', |
204 | 'database' => 'database', |
205 | 'alias' => 'alias', |
206 | ]; |
207 | |
208 | foreach ($params as $key => $val) |
209 | { |
210 | if (( ! array_key_exists($key, $skip)) && ! empty($val)) |
211 | { |
212 | $pairs[] = implode('=', [$key, $val]); |
213 | } |
214 | } |
215 | |
216 | return strtolower($dbType) . ':' . implode(';', $pairs); |
217 | } |
218 | } |