Completed
Push — master ( 25342a...843744 )
by Nazar
04:25
created

PostgreSQL::n()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 7
rs 9.4285
1
<?php
2
/**
3
 * @package   CleverStyle CMS
4
 * @author    Nazar Mokrynskyi <[email protected]>
5
 * @copyright Copyright (c) 2011-2016, Nazar Mokrynskyi
6
 * @license   MIT License, see license.txt
7
 */
8
namespace cs\DB;
9
class PostgreSQL extends _Abstract {
10
	/**
11
	 * @var resource DB connection handler
12
	 */
13
	protected $handler;
14
	/**
15
	 * @var resource
16
	 */
17
	protected $query_result;
18
	/**
19
	 * @inheritdoc
20
	 */
21
	function __construct ($database, $user = '', $password = '', $host = 'localhost', $charset = 'UTF8', $prefix = '') {
22
		$start = microtime(true);
23
		/**
24
		 * Parsing of $host variable, detecting port and persistent connection
25
		 */
26
		list($host, $port, $persistent) = $this->get_host_port_and_persistent($host);
27
		$connection_string = "host=$host port=$port dbname=$database user=$user password=$password options='--client_encoding=$charset'";
28
		$this->handler     = $persistent ? pg_connect($connection_string) : pg_pconnect($connection_string);
29
		if (!is_resource($this->handler)) {
30
			return;
31
		}
32
		$this->database        = $database;
33
		$this->connected       = true;
34
		$this->connecting_time = microtime(true) - $start;
35
		$this->db_type         = 'postgresql';
36
		$this->prefix          = $prefix;
37
	}
38
	/**
39
	 * Parse host string into host, port and persistent separately
40
	 *
41
	 * Understands `p:` prefix for persistent connections
42
	 *
43
	 * @param string $host_string
44
	 *
45
	 * @return array
46
	 */
47
	protected function get_host_port_and_persistent ($host_string) {
48
		$host       = explode(':', $host_string);
49
		$port       = 5432;
50
		$persistent = false;
51
		switch (count($host)) {
52
			case 1:
53
				$host = $host[0];
54
				break;
55
			case 2:
56
				if ($host[0] == 'p') {
57
					$persistent = true;
58
					$host       = $host[1];
59
				} else {
60
					list($host, $port) = $host;
61
				}
62
				break;
63
			case 3:
64
				$persistent = true;
65
				list(, $host, $port) = $host;
66
		}
67
		return [$host, $port, $persistent];
68
	}
69
	/**
70
	 * @inheritdoc
71
	 */
72
	function q ($query, $params = [], ...$param) {
73
		// Hack to convert small subset of MySQL queries into PostgreSQL-compatible syntax
74
		$query = str_replace('`', '"', $query);
75
		$query = preg_replace_callback(
76
			'/(INSERT IGNORE INTO|REPLACE INTO)(.+)(;|$)/Uis',
77
			function ($matches) {
78
				// Only support simplest cases
79
				if (stripos($matches[2], 'on duplicate')) {
80
					return $matches[0];
81
				}
82
				switch (strtoupper($matches[1])) {
83
					case 'INSERT IGNORE INTO':
84
						return "INSERT INTO $matches[2] ON CONFLICT DO NOTHING$matches[3]";
85
					case 'REPLACE INTO':
86
						$table_name = substr(
87
							$matches[2],
88
							strpos($matches[2], '"') + 1
89
						);
90
						$table_name = substr(
91
							$table_name,
92
							0,
93
							strpos($table_name, '"')
94
						);
95
						$update     = preg_replace_callback(
96
							'/"([^"]+)"/',
97
							function ($matches) {
98
								return "\"$matches[1]\" = EXCLUDED.\"$matches[1]\"";
99
							},
100
							substr(
101
								strstr($matches[2], ')', true),
102
								strpos($matches[2], '(') + 1
103
							)
104
						);
105
						// Only support constraint named as table with `_primary` prefix
106
						return "INSERT INTO $matches[2] ON CONFLICT ON CONSTRAINT \"{$table_name}_primary\" DO UPDATE SET $update$matches[3]";
107
				}
108
			},
109
			$query
110
		);
111
		return parent::q(...([$query] + func_get_args()));
112
	}
113
	/**
114
	 * @inheritdoc
115
	 *
116
	 * @return false|resource
117
	 */
118
	protected function q_internal ($query) {
119
		if (!$query) {
120
			return false;
121
		}
122
		return $this->query_result = pg_query($this->handler, $query);
123
	}
124
	/**
125
	 * @inheritdoc
126
	 */
127
	protected function q_multi_internal ($query) {
128
		return $this->q_internal(implode(';', $query));
129
	}
130
	/**
131
	 * @deprecated
132
	 * @todo remove after 4.x release
133
	 *
134
	 * @inheritdoc
135
	 */
136
	function n ($query_result) {
137
		if (is_resource($query_result)) {
138
			return pg_num_rows($query_result);
139
		} else {
140
			return false;
141
		}
142
	}
143
	/**
144
	 * @inheritdoc
145
	 *
146
	 * @param false|resource $query_result
147
	 */
148
	function f ($query_result, $single_column = false, $array = false, $indexed = false) {
149
		if (!is_resource($query_result)) {
150
			return false;
151
		}
152
		$result_type = $single_column || $indexed ? PGSQL_NUM : PGSQL_ASSOC;
153
		if ($array) {
154
			$result = [];
155
			while ($current = pg_fetch_array($query_result, null, $result_type)) {
156
				$result[] = $single_column ? $current[0] : $current;
157
			}
158
			$this->free($query_result);
159
			return $result;
160
		}
161
		$result = pg_fetch_array($query_result, null, $result_type);
162
		return $single_column && $result ? $result[0] : $result;
163
	}
164
	/**
165
	 * @inheritdoc
166
	 */
167
	function id () {
168
		return (int)$this->qfs('SELECT lastval()');
169
	}
170
	/**
171
	 * @inheritdoc
172
	 */
173
	function affected () {
174
		return is_resource($this->query_result) ? pg_affected_rows($this->query_result) : 0;
175
	}
176
	/**
177
	 * @inheritdoc
178
	 *
179
	 * @param false|resource $query_result
180
	 */
181
	function free ($query_result) {
182
		if (is_resource($query_result)) {
183
			return pg_free_result($query_result);
184
		}
185
		return true;
186
	}
187
	/**
188
	 * @inheritdoc
189
	 */
190
	function columns ($table, $like = false) {
191
		if (!$table) {
192
			return false;
193
		}
194
		if ($like) {
195
			$like    = $this->s($like);
1 ignored issue
show
Documentation introduced by
$like is of type boolean, but the function expects a string|array<integer,string>.

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...
196
			$columns = $this->qfas(
197
				"SELECT `column_name` 
198
				FROM `information_schema`.`columns`
199
				WHERE
200
					`table_name` = '$table' AND
201
					`column_name` LIKE $like"
202
			) ?: [];
203
		} else {
204
			$columns = $this->qfas(
205
				"SELECT `column_name`
206
				FROM `information_schema`.`columns`
207
				WHERE `table_name` = '$table'"
208
			) ?: [];
209
		}
210
		return $columns;
211
	}
212
	/**
213
	 * @inheritdoc
214
	 */
215
	function tables ($like = false) {
216
		if ($like) {
217
			$like = $this->s($like);
1 ignored issue
show
Documentation introduced by
$like is of type boolean, but the function expects a string|array<integer,string>.

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...
218
			return $this->qfas(
219
				"SELECT `table_name`
220
				FROM `information_schema`.`tables`
221
				WHERE
222
					`table_schema` = 'public' AND
223
					`table_name` LIKE $like
224
				ORDER BY `table_name` ASC"
225
			) ?: [];
226
		} else {
227
			return $this->qfas(
228
				"SELECT `table_name`
229
				FROM `information_schema`.`tables`
230
				WHERE `table_schema` = 'public'
231
				ORDER BY `table_name` ASC"
232
			) ?: [];
233
		}
234
	}
235
	/**
236
	 * @inheritdoc
237
	 */
238
	protected function s_internal ($string, $single_quotes_around) {
239
		return $single_quotes_around ? pg_escape_literal($this->handler, $string) : pg_escape_string($this->handler, $string);
240
	}
241
	/**
242
	 * @inheritdoc
243
	 */
244
	function server () {
245
		return $this->handler->server_info;
246
	}
247
	/**
248
	 * @inheritdoc
249
	 */
250
	function __destruct () {
251
		if ($this->connected && is_resource($this->handler)) {
252
			pg_close($this->handler);
253
			$this->connected = false;
254
		}
255
	}
256
}
257