ConnectionManager::createDsn()   A
last analyzed

Complexity

Conditions 5
Paths 6

Size

Total Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
nc 6
nop 2
dl 0
loc 30
rs 9.1288
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/**
3
 * Query
4
 *
5
 * SQL Query Builder / Database Abstraction Layer
6
 *
7
 * PHP version 7.1
8
 *
9
 * @package     Query
10
 * @author      Timothy J. Warren <[email protected]>
11
 * @copyright   2012 - 2018 Timothy J. Warren
12
 * @license     http://www.opensource.org/licenses/mit-license.html  MIT License
13
 * @link        https://git.timshomepage.net/aviat4ion/Query
14
 */
15
namespace Query;
16
17
use DomainException;
18
19
/**
20
 * Connection manager class to manage connections for the
21
 * Query method
22
 */
23
final class ConnectionManager {
24
25
	/**
26
	 * Map of named database connections
27
	 * @var array
28
	 */
29
	private $connections = [];
30
31
	/**
32
	 * Class instance variable
33
	 * @var ConnectionManager|null
34
	 */
35
	private static $instance;
36
37
	/**
38
	 * Private constructor to prevent multiple instances
39
	 * @codeCoverageIgnore
40
	 */
41
	private function __construct()
42
	{
43
	}
44
45
	/**
46
	 * Private clone method to prevent cloning
47
	 *
48
	 * @throws DomainException
49
	 * @return void
50
	 */
51
	public function __clone()
52
	{
53
		throw new DomainException("Can't clone singleton");
54
	}
55
56
	/**
57
	 * Prevent serialization of this object
58
	 *
59
	 * @throws DomainException
60
	 * @return void
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
	 * @return void
72
	 */
73
	public function __wakeup()
74
	{
75
		throw new DomainException("Can't unserialize singleton");
76
	}
77
78
	/**
79
	 * Return  a connection manager instance
80
	 *
81
	 * @staticvar null $instance
82
	 * @return ConnectionManager
83
	 */
84
	public static function getInstance(): ConnectionManager
85
	{
86
		if (self::$instance === NULL)
87
		{
88
			self::$instance = new self();
89
		}
90
91
		return self::$instance;
92
	}
93
94
	/**
95
	 * Returns the connection specified by the name given
96
	 *
97
	 * @param string|array|object $name
98
	 * @return QueryBuilderInterface
99
	 * @throws Exception\NonExistentConnectionException
100
	 */
101
	public function getConnection($name = ''): QueryBuilderInterface
102
	{
103
		// If the parameter is a string, use it as an array index
104
		if (is_scalar($name) && isset($this->connections[$name]))
105
		{
106
			return $this->connections[$name];
107
		}
108
		else if (empty($name) && ! empty($this->connections)) // Otherwise, return the last one
109
		{
110
			return end($this->connections);
111
		}
112
113
		// You should actually connect before trying to get a connection...
114
		throw new Exception\NonExistentConnectionException('The specified connection does not exist');
115
	}
116
117
	/**
118
	 * Parse the passed parameters and return a connection
119
	 *
120
	 * @param object|array $params
121
	 * @throws Exception\BadDBDriverException
122
	 * @return QueryBuilderInterface
123
	 */
124
	public function connect($params): QueryBuilderInterface
125
	{
126
		[$dsn, $dbtype, $params, $options] = $this->parseParams($params);
0 ignored issues
show
Bug introduced by
The variable $dsn does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Bug introduced by
The variable $dbtype seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
Bug introduced by
The variable $options does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
Documentation introduced by
$params is of type object|array, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
127
128
		$dbtype = ucfirst($dbtype);
0 ignored issues
show
Bug introduced by
The variable $dbtype seems only to be defined at a later point. Did you maybe move this code here without moving the variable definition?

This error can happen if you refactor code and forget to move the variable initialization.

Let’s take a look at a simple example:

function someFunction() {
    $x = 5;
    echo $x;
}

The above code is perfectly fine. Now imagine that we re-order the statements:

function someFunction() {
    echo $x;
    $x = 5;
}

In that case, $x would be read before it is initialized. This was a very basic example, however the principle is the same for the found issue.

Loading history...
129
		$driver = "\\Query\\Drivers\\{$dbtype}\\Driver";
130
131
		// Create the database connection
132
		$db =  ! empty($params->user)
133
			? new $driver($dsn, $params->user, $params->pass, $options)
134
			: new $driver($dsn, '', '', $options);
135
136
		// Set the table prefix, if it exists
137
		if (isset($params->prefix))
138
		{
139
			$db->setTablePrefix($params->prefix);
140
		}
141
142
		// Create Query Builder object
143
		$conn = new QueryBuilder($db, new QueryParser($db));
144
145
146
		// Save it for later
147
		if (isset($params->alias))
148
		{
149
			$this->connections[$params->alias] = $conn;
150
		}
151
		else
152
		{
153
			$this->connections[] = $conn;
154
		}
155
156
		return $conn;
157
	}
158
159
	/**
160
	 * Parses params into a dsn and option array
161
	 *
162
	 * @param \stdClass $params
163
	 * @return object|array
164
	 * @throws Exception\BadDBDriverException
165
	 */
166
	public function parseParams($params): array
167
	{
168
		$params = (object) $params;
169
		$params->type = strtolower($params->type);
170
		$dbtype = ($params->type !== 'postgresql') ? $params->type : 'pgsql';
171
		$dbtype = ucfirst($dbtype);
172
173
		// Make sure the class exists
174
		if ( ! class_exists("\\Query\\Drivers\\{$dbtype}\\Driver"))
175
		{
176
			throw new Exception\BadDBDriverException('Database driver does not exist, or is not supported');
177
		}
178
179
		// Set additional PDO options
180
		$options = [];
181
182
		if (isset($params->options))
183
		{
184
			$options = (array) $params->options;
185
		}
186
187
		// Create the dsn for the database to connect to
188
		if(strtolower($dbtype) === 'sqlite')
189
		{
190
			$dsn = $params->file;
191
		}
192
		else
193
		{
194
			$dsn = $this->createDsn($dbtype, $params);
195
		}
196
197
198
		return [$dsn, $dbtype, $params, $options];
199
	}
200
201
	/**
202
	 * Create the dsn from the db type and params
203
	 *
204
	 * @codeCoverageIgnore
205
	 * @param string $dbtype
206
	 * @param array|object $params
207
	 * @return string
208
	 */
209
	private function createDsn(string $dbtype, $params): string
210
	{
211
		$pairs = [];
212
213
		if ( ! empty($params->database))
214
		{
215
			$pairs[] = implode('=', ['dbname', $params->database]);
216
		}
217
218
		$skip = [
219
			'name' => 'name',
220
			'pass' => 'pass',
221
			'user' => 'user',
222
			'type' => 'type',
223
			'prefix' => 'prefix',
224
			'options' => 'options',
225
			'database' => 'database',
226
			'alias' => 'alias'
227
		];
228
229
		foreach($params as $key => $val)
230
		{
231
			if (( ! array_key_exists($key, $skip)) &&  ! empty($val))
232
			{
233
				$pairs[] = implode('=', [$key, $val]);
234
			}
235
		}
236
237
		return strtolower($dbtype) . ':' . implode(';', $pairs);
238
	}
239
}