1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* Class PostgreSQLDriver |
4
|
|
|
* |
5
|
|
|
* @filesource PostgreSQLDriver.php |
6
|
|
|
* @created 21.02.2016 |
7
|
|
|
* @package chillerlan\Database\Drivers\PostgreSQL |
8
|
|
|
* @author Smiley <[email protected]> |
9
|
|
|
* @copyright 2016 Smiley |
10
|
|
|
* @license MIT |
11
|
|
|
*/ |
12
|
|
|
|
13
|
|
|
namespace chillerlan\Database\Drivers; |
14
|
|
|
|
15
|
|
|
use chillerlan\Database\DBResult; |
16
|
|
|
|
17
|
|
|
class PostgreSQLDriver extends DBDriverAbstract{ |
18
|
|
|
|
19
|
|
|
protected $dialect = 'pgsql'; |
20
|
|
|
protected $quotes = ['"', '"']; |
21
|
|
|
|
22
|
|
|
/** |
23
|
|
|
* Holds the database resource object |
24
|
|
|
* |
25
|
|
|
* @var resource |
26
|
|
|
*/ |
27
|
|
|
protected $db; |
28
|
|
|
|
29
|
|
|
/** |
30
|
|
|
* Establishes a database connection and returns the connection object |
31
|
|
|
* |
32
|
|
|
* @return \chillerlan\Database\Drivers\DBDriverInterface |
33
|
|
|
* @throws \chillerlan\Database\Drivers\DBDriverException |
34
|
|
|
*/ |
35
|
|
|
public function connect():DBDriverInterface{ |
36
|
|
|
|
37
|
|
|
if(gettype($this->db) === 'resource'){ |
38
|
|
|
return $this; |
39
|
|
|
} |
40
|
|
|
|
41
|
|
|
// i am an ugly duckling. fix me please. |
42
|
|
|
|
43
|
|
|
$options = [ |
44
|
|
|
'--client_encoding='.$this->options->pgsql_charset, |
45
|
|
|
]; |
46
|
|
|
|
47
|
|
|
$conn_str = [ |
48
|
|
|
'host=\''.$this->options->host.'\'', |
49
|
|
|
'port=\''.(int)$this->options->port.'\'', |
50
|
|
|
'dbname=\''.$this->options->database.'\'', |
51
|
|
|
'user=\''.$this->options->username.'\'', |
52
|
|
|
'password=\''.$this->options->password.'\'', |
53
|
|
|
'options=\''.implode(' ', $options).'\'', |
54
|
|
|
]; |
55
|
|
|
|
56
|
|
|
try{ |
57
|
|
|
$this->db = pg_connect(implode(' ', $conn_str)); |
58
|
|
|
|
59
|
|
|
return $this; |
60
|
|
|
} |
61
|
|
|
catch(\Exception $e){ |
62
|
|
|
throw new DBDriverException('db error: [PostgreSQLDriver]: '.$e->getMessage()); |
63
|
|
|
} |
64
|
|
|
|
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Closes a database connection |
69
|
|
|
* |
70
|
|
|
* @return bool |
71
|
|
|
*/ |
72
|
|
|
public function disconnect():bool{ |
73
|
|
|
return pg_close($this->db); |
74
|
|
|
} |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* Returns info about the used php client |
78
|
|
|
* |
79
|
|
|
* @return string php's database client string |
80
|
|
|
*/ |
81
|
|
|
public function getClientInfo():string{ |
82
|
|
|
$ver = pg_version($this->db); |
83
|
|
|
|
84
|
|
|
return 'PostgreSQL '.$ver['client'].' ('.$ver['client_encoding'].')'; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* Returns info about the database server |
89
|
|
|
* |
90
|
|
|
* @return string database's serverinfo string |
91
|
|
|
*/ |
92
|
|
|
public function getServerInfo():string{ |
93
|
|
|
$ver = pg_version($this->db); |
94
|
|
|
|
95
|
|
|
return 'PostgreSQL '.$ver['server'].' ('.$ver['server_encoding'].', date style: '.$ver['DateStyle'].', time zone: '.$ver['TimeZone'].')'; |
96
|
|
|
} |
97
|
|
|
|
98
|
|
|
/** |
99
|
|
|
* @param $data |
100
|
|
|
* |
101
|
|
|
* @return string |
102
|
|
|
*/ |
103
|
|
|
public function escape($data){ |
104
|
|
|
return pg_escape_string($this->db, $data); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* @param $result |
109
|
|
|
* @param string|null $index |
110
|
|
|
* @param bool $assoc |
111
|
|
|
* |
112
|
|
|
* @return bool|\chillerlan\Database\DBResult |
113
|
|
|
*/ |
114
|
|
|
protected function __getResult($result, string $index = null, bool $assoc = true){ |
115
|
|
|
|
116
|
|
|
if(is_bool($result)){ |
117
|
|
|
return $result; // @codeCoverageIgnore |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
$out = new DBResult(null, $this->source_encoding, $this->dest_encoding); |
121
|
|
|
$i = 0; |
122
|
|
|
|
123
|
|
|
while($row = call_user_func_array($assoc ? 'pg_fetch_assoc' : 'pg_fetch_row', [$result])){ |
124
|
|
|
$key = $i; |
125
|
|
|
|
126
|
|
|
$j = 0; |
127
|
|
|
foreach($row as $k => $item){ |
128
|
|
|
// https://gitter.im/arenanet/api-cdi?at=594326ba31f589c64fafe554 |
129
|
|
|
if(pg_field_type($result, $j) === 'boolean'){ |
130
|
|
|
$row[$k] = $item === 't'; |
131
|
|
|
} |
132
|
|
|
|
133
|
|
|
$j++; |
134
|
|
|
} |
135
|
|
|
|
136
|
|
|
if($assoc && !empty($index)){ |
137
|
|
|
$key = $row[$index]; |
138
|
|
|
} |
139
|
|
|
|
140
|
|
|
$out[$key] = $row; |
141
|
|
|
$i++; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
$r = $i === 0 ? true : $out; |
145
|
|
|
|
146
|
|
|
pg_free_result($result); |
147
|
|
|
|
148
|
|
|
return $r; |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* @param string $sql |
153
|
|
|
* @param string|null $index |
154
|
|
|
* @param bool $assoc |
155
|
|
|
* |
156
|
|
|
* @return bool|\chillerlan\Database\DBResult |
157
|
|
|
*/ |
158
|
|
|
protected function __raw(string $sql, string $index = null, bool $assoc = true){ |
159
|
|
|
return $this->__getResult(pg_query($sql), $index, $assoc); |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
/** |
163
|
|
|
* @param string $sql |
164
|
|
|
* @param array $values |
165
|
|
|
* @param string|null $index |
166
|
|
|
* @param bool $assoc |
167
|
|
|
* |
168
|
|
|
* @return bool|\chillerlan\Database\DBResult |
169
|
|
|
*/ |
170
|
|
|
protected function __prepared(string $sql, array $values = [], string $index = null, bool $assoc = true){ |
171
|
|
|
pg_prepare($this->db, '', $this->replaceParams($sql)); |
172
|
|
|
|
173
|
|
|
return $this->__getResult(pg_execute($this->db, '', $values), $index, $assoc); |
174
|
|
|
} |
175
|
|
|
|
176
|
|
|
/** |
177
|
|
|
* @param string $sql |
178
|
|
|
* @param array $values |
179
|
|
|
* |
180
|
|
|
* @return bool |
181
|
|
|
*/ |
182
|
|
View Code Duplication |
protected function __multi(string $sql, array $values){ |
|
|
|
|
183
|
|
|
pg_prepare($this->db, '', $this->replaceParams($sql)); |
184
|
|
|
|
185
|
|
|
foreach($values as $row){ |
186
|
|
|
pg_execute($this->db, '', $row); |
187
|
|
|
} |
188
|
|
|
|
189
|
|
|
return true; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
/** |
193
|
|
|
* @param string $sql |
194
|
|
|
* @param array $data |
195
|
|
|
* @param $callback |
196
|
|
|
* |
197
|
|
|
* @return bool |
198
|
|
|
*/ |
199
|
|
View Code Duplication |
protected function __multi_callback(string $sql, array $data, $callback){ |
|
|
|
|
200
|
|
|
pg_prepare($this->db, '', $this->replaceParams($sql)); |
201
|
|
|
|
202
|
|
|
foreach($data as $row){ |
203
|
|
|
pg_execute($this->db, '', call_user_func_array($callback, [$row])); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
return true; |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
protected function replaceParams(string $sql):string{ |
210
|
|
|
$i = 0; |
211
|
|
|
|
212
|
|
|
return preg_replace_callback('/(\?)/', function() use (&$i){ |
213
|
|
|
return '$'.++$i; |
214
|
|
|
}, $sql); |
215
|
|
|
} |
216
|
|
|
|
217
|
|
|
} |
218
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.