Test Failed
Push — master ( e9c576...010f10 )
by Joe
03:45
created

Db   C

Complexity

Total Complexity 56

Size/Duplication

Total Lines 366
Duplicated Lines 0 %

Test Coverage

Coverage 0%

Importance

Changes 0
Metric Value
eloc 106
dl 0
loc 366
ccs 0
cts 123
cp 0
rs 5.5199
c 0
b 0
f 0
wmc 56

25 Methods

Rating   Name   Duplication   Size   Complexity  
A tableNames() 0 11 2
A real_escape() 0 2 1
A selectDb() 0 1 1
B query() 0 27 7
A ifadd() 0 4 2
A num_rows() 0 2 1
A transactionCommit() 0 5 2
A seek() 0 2 1
A transactionBegin() 0 2 1
A lock() 0 15 4
A num_fields() 0 2 1
A free() 0 3 1
A disconnect() 0 2 1
A qr() 0 2 1
A transactionAbort() 0 2 1
A unlock() 0 2 1
A escape() 0 2 1
A next_record() 0 12 4
A useDb() 0 2 1
A affectedRows() 0 2 1
A indexNames() 0 11 2
A queryReturn() 0 12 4
B getLastInsertId() 0 26 8
A connect() 0 7 4
A haltmsg() 0 5 3

How to fix   Complexity   

Complex Class

Complex classes like Db often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Db, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * PostgreSQL Related Functionality
4
 * @author Joe Huss <[email protected]>
5
 * @copyright 2018
6
 * @package MyAdmin
7
 * @category SQL
8
 */
9
10
namespace MyDb\Pgsql;
11
12
use \MyDb\Generic;
13
use \MyDb\Db_Interface;
14
15
/**
16
 * Db
17
 *
18
 * @access public
19
 */
20
class Db extends Generic implements Db_Interface {
21
	/* public: this is an api revision, not a CVS revision. */
22
	public $type = 'pgsql';
23
	public $port = '5432';
24
25
	/**
26
	 * Db::ifadd()
27
	 *
28
	 * @param mixed $add
29
	 * @param mixed $me
30
	 * @return string
31
	 */
32
	public function ifadd($add, $me) {
33
		if ('' != $add)
34
			return ' '.$me.$add;
35
		return '';
36
	}
37
38
	/**
39
	 * @param $string
40
	 * @return string
41
	 */
42
	public function real_escape($string) {
43
		return escapeshellarg($string);
44
	}
45
46
	/**
47
	 * @param $string
48
	 * @return string
49
	 */
50
	public function escape($string) {
51
		return escapeshellarg($string);
52
	}
53
54
	/**
55
	 * alias function of select_db, changes the database we are working with.
56
	 *
57
	 * @param string $database the name of the database to use
58
	 * @return void
59
	 */
60
	public function useDb($database) {
61
		$this->selectDb($database);
62
	}
63
64
	/**
65
	 * changes the database we are working with.
66
	 *
67
	 * @param string $database the name of the database to use
68
	 * @return void
69
	 */
70
	public function selectDb($database) {
0 ignored issues
show
Unused Code introduced by
The parameter $database is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

70
	public function selectDb(/** @scrutinizer ignore-unused */ $database) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
71
		/*if ($database != $this->database) {
72
			$this->database = $database;
73
			$this->linkId = null;
74
			$this->connect();
75
		}*/
76
	}
77
78
	/**
79
	 * Db::connect()
80
	 * @return void
81
	 */
82
	public function connect() {
83
		if (0 == $this->linkId) {
84
			$connectString = 'dbname='.$this->database.$this->ifadd($this->host, 'host=').$this->ifadd($this->port, 'port=').$this->ifadd($this->user, 'user=').$this->ifadd("'".$this->password."'", 'password=');
85
			$this->linkId = pg_pconnect($connectString);
86
87
			if (!$this->linkId) {
88
				$this->halt('Link-ID == FALSE, '.($GLOBALS['phpgw_info']['server']['db_persistent'] ? 'p' : '').'connect failed');
89
			}
90
		}
91
	}
92
93
	/* This only affects systems not using persistent connections */
94
95
	/**
96
	 * Db::disconnect()
97
	 * @return bool
98
	 */
99
	public function disconnect() {
100
		return @pg_close($this->linkId);
0 ignored issues
show
Bug introduced by
$this->linkId of type integer is incompatible with the type resource expected by parameter $connection of pg_close(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

100
		return @pg_close(/** @scrutinizer ignore-type */ $this->linkId);
Loading history...
101
	}
102
103
	/**
104
	 * Db::queryReturn()
105
	 *
106
	 * Sends an SQL query to the server like the normal query() command but iterates through
107
	 * any rows and returns the row or rows immediately or FALSE on error
108
	 *
109
	 * @param mixed $query SQL Query to be used
110
	 * @param string $line optionally pass __LINE__ calling the query for logging
111
	 * @param string $file optionally pass __FILE__ calling the query for logging
112
	 * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only
113
	 */
114
	public function queryReturn($query, $line = '', $file = '') {
115
		$this->query($query, $line, $file);
116
		if ($this->num_rows() == 0) {
117
			return false;
118
		} elseif ($this->num_rows() == 1) {
119
			$this->next_record(MYSQL_ASSOC);
0 ignored issues
show
introduced by
The constant MYSQL_ASSOC has been deprecated: 5.5 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

119
			$this->next_record(/** @scrutinizer ignore-deprecated */ MYSQL_ASSOC);
Loading history...
120
			return $this->Record;
121
		} else {
122
			$out = [];
123
			while ($this->next_record(MYSQL_ASSOC))
0 ignored issues
show
introduced by
The constant MYSQL_ASSOC has been deprecated: 5.5 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

123
			while ($this->next_record(/** @scrutinizer ignore-deprecated */ MYSQL_ASSOC))
Loading history...
124
				$out[] = $this->Record;
125
			return $out;
126
		}
127
	}
128
129
	/**
130
	 * db:qr()
131
	 *
132
	 *  alias of queryReturn()
133
	 *
134
	 * @param mixed $query SQL Query to be used
135
	 * @param string $line optionally pass __LINE__ calling the query for logging
136
	 * @param string $file optionally pass __FILE__ calling the query for logging
137
	 * @return mixed FALSE if no rows, if a single row it returns that, if multiple it returns an array of rows, associative responses only
138
	 */
139
	public function qr($query, $line = '', $file = '') {
140
		return $this->queryReturn($query, $line, $file);
141
	}
142
143
	/**
144
	 * Db::query()
145
	 *
146
	 *  Sends an SQL query to the database
147
	 *
148
	 * @param mixed $queryString
149
	 * @param string $line
150
	 * @param string $file
151
	 * @return mixed 0 if no query or query id handler, safe to ignore this return
152
	 */
153
	public function query($queryString, $line = '', $file = '') {
154
		if (!$line && !$file) {
155
			if (isset($GLOBALS['tf']))
156
				$GLOBALS['tf']->warning(__LINE__, __FILE__, "Lazy developer didn't pass __LINE__ and __FILE__ to db->query() - Actually query: $queryString");
157
		}
158
159
		/* No empty queries, please, since PHP4 chokes on them. */
160
		/* The empty query string is passed on from the constructor,
161
		* when calling the class without a query, e.g. in situations
162
		* like these: '$db = new db_Subclass;'
163
		*/
164
		if ($queryString == '')
165
			return 0;
166
167
		$this->connect();
168
169
		/* printf("<br>Debug: query = %s<br>\n", $queryString); */
170
171
		$this->queryId = @pg_exec($this->linkId, $queryString);
172
		$this->Row = 0;
173
174
		$this->Error = pg_errormessage($this->linkId);
175
		$this->Errno = ($this->Error == '') ? 0 : 1;
176
		if (!$this->queryId)
177
			$this->halt('Invalid SQL: '.$queryString, $line, $file);
178
179
		return $this->queryId;
180
	}
181
182
	/**
183
	 * Db::free()
184
	 *
185
	 * @return void
186
	 */
187
	public function free() {
188
		@pg_freeresult($this->queryId);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for pg_freeresult(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

188
		/** @scrutinizer ignore-unhandled */ @pg_freeresult($this->queryId);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
189
		$this->queryId = 0;
190
	}
191
192
	/**
193
	 * Db::next_record()
194
	 * @param mixed $resultType
195
	 * @return bool
196
	 */
197
	public function next_record($resultType = PGSQL_BOTH) {
198
		$this->Record = @pg_fetch_array($this->queryId, $this->Row++, $resultType);
0 ignored issues
show
Bug introduced by
$this->queryId of type integer is incompatible with the type resource expected by parameter $result of pg_fetch_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

198
		$this->Record = @pg_fetch_array(/** @scrutinizer ignore-type */ $this->queryId, $this->Row++, $resultType);
Loading history...
199
200
		$this->Error = pg_errormessage($this->linkId);
201
		$this->Errno = ($this->Error == '') ? 0 : 1;
202
203
		$stat = is_array($this->Record);
204
		if (!$stat && $this->autoFree) {
0 ignored issues
show
introduced by
The condition $stat is always true.
Loading history...
205
			pg_freeresult($this->queryId);
206
			$this->queryId = 0;
207
		}
208
		return $stat;
209
	}
210
211
	/**
212
	 * Db::seek()
213
	 *
214
	 * @param mixed $pos
215
	 * @return void
216
	 */
217
	public function seek($pos) {
218
		$this->Row = $pos;
219
	}
220
221
	/**
222
	 * Db::transactionBegin()
223
	 *
224
	 * @return mixed
225
	 */
226
	public function transactionBegin() {
227
		return $this->query('begin');
228
	}
229
230
	/**
231
	 * Db::transactionCommit()
232
	 * @return bool|mixed
233
	 */
234
	public function transactionCommit() {
235
		if (!$this->Errno) {
236
			return pg_exec($this->linkId, 'commit');
237
		} else {
238
			return false;
239
		}
240
	}
241
242
	/**
243
	 * Db::transactionAbort()
244
	 * @return mixed
245
	 */
246
	public function transactionAbort() {
247
		return pg_exec($this->linkId, 'rollback');
248
	}
249
250
	/**
251
	 * Db::getLastInsertId()
252
	 * @param mixed $table
253
	 * @param mixed $field
254
	 * @return int
255
	 */
256
	public function getLastInsertId($table, $field) {
257
		/* This will get the last insert ID created on the current connection.  Should only be called
258
		* after an insert query is run on a table that has an auto incrementing field.  Of note, table
259
		* and field are required because pgsql returns the last inserted OID, which is unique across
260
		* an entire installation.  These params allow us to retrieve the sequenced field without adding
261
		* conditional code to the apps.
262
		*/
263
		if (!isset($table) || $table == '' || !isset($field) || $field == '')
264
			return -1;
265
266
		$oid = pg_getlastoid($this->queryId);
267
		if ($oid == -1)
268
			return -1;
269
270
		$result = @pg_exec($this->linkId, "select $field from $table where oid=$oid");
271
		if (!$result)
272
			return -1;
273
274
		$Record = @pg_fetch_array($result, 0);
275
		@pg_freeresult($result);
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition for pg_freeresult(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

275
		/** @scrutinizer ignore-unhandled */ @pg_freeresult($result);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
276
		if (!is_array($Record)) /* OID not found? */
0 ignored issues
show
introduced by
The condition is_array($Record) is always true.
Loading history...
277
		{
278
			return -1;
279
		}
280
281
		return $Record[0];
282
	}
283
284
	/**
285
	 * Db::lock()
286
	 * @param mixed  $table
287
	 * @param string $mode
288
	 * @return int|mixed
289
	 */
290
	public function lock($table, $mode = 'write') {
291
		$result = $this->transactionBegin();
292
293
		if ($mode == 'write') {
294
			if (is_array($table)) {
295
				while ($t = each($table))
0 ignored issues
show
Deprecated Code introduced by
The function each() has been deprecated: 7.2 ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

295
				while ($t = /** @scrutinizer ignore-deprecated */ each($table))

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
296
					$result = pg_exec($this->linkId, 'lock table '.$t[1].' in share mode');
297
			} else {
298
				$result = pg_exec($this->linkId, 'lock table '.$table.' in share mode');
299
			}
300
		} else {
301
			$result = 1;
302
		}
303
304
		return $result;
305
	}
306
307
	/**
308
	 * Db::unlock()
309
	 * @return bool|mixed
310
	 */
311
	public function unlock() {
312
		return $this->transactionCommit();
313
	}
314
315
	/**
316
	 * Db::affectedRows()
317
	 * @return void
318
	 */
319
	public function affectedRows() {
320
		return pg_cmdtuples($this->queryId);
321
	}
322
323
	/**
324
	 * Db::num_rows()
325
	 * @return int
326
	 */
327
	public function num_rows() {
328
		return pg_numrows($this->queryId);
329
	}
330
331
	/**
332
	 * Db::num_fields()
333
	 * @return int
334
	 */
335
	public function num_fields() {
336
		return pg_numfields($this->queryId);
337
	}
338
339
	/**
340
	 * @param mixed $msg
341
	 * @param string $line
342
	 * @param string $file
343
	 * @return mixed|void
344
	 */
345
	public function haltmsg($msg, $line = '', $file = '') {
346
		$this->log("Database error: $msg", $line, $file, 'error');
347
		if ($this->Errno != '0' || $this->Error != '()')
348
			$this->log('PostgreSQL Error: '.pg_last_error($this->linkId), $line, $file, 'error');
0 ignored issues
show
Bug introduced by
$this->linkId of type integer is incompatible with the type resource expected by parameter $connection of pg_last_error(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

348
			$this->log('PostgreSQL Error: '.pg_last_error(/** @scrutinizer ignore-type */ $this->linkId), $line, $file, 'error');
Loading history...
349
		$this->logBackTrace($msg, $line, $file);
350
	}
351
352
	/**
353
	 * Db::tableNames()
354
	 *
355
	 * @return array
356
	 */
357
	public function tableNames() {
358
		$return = [];
359
		$this->query("select relname from pg_class where relkind = 'r' and not relname like 'pg_%'");
360
		$i = 0;
361
		while ($this->next_record()) {
362
			$return[$i]['table_name'] = $this->f(0);
363
			$return[$i]['tablespace_name'] = $this->database;
364
			$return[$i]['database'] = $this->database;
365
			++$i;
366
		}
367
		return $return;
368
	}
369
370
	/**
371
	 * Db::indexNames()
372
	 *
373
	 * @return array
374
	 */
375
	public function indexNames() {
376
		$return = [];
377
		$this->query("SELECT relname FROM pg_class WHERE NOT relname ~ 'pg_.*' AND relkind ='i' ORDER BY relname");
378
		$i = 0;
379
		while ($this->next_record()) {
380
			$return[$i]['index_name'] = $this->f(0);
381
			$return[$i]['tablespace_name'] = $this->database;
382
			$return[$i]['database'] = $this->database;
383
			++$i;
384
		}
385
		return $return;
386
	}
387
}
388