1 <?php
2 3 4 5 6 7 8 9 10
11
12 namespace Neevo\Drivers;
13
14 use DateTime;
15 use InvalidArgumentException;
16 use Neevo\BaseStatement;
17 use Neevo\DriverException;
18 use Neevo\DriverInterface;
19 use Neevo\Manager;
20 use Neevo\Parser;
21
22
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
41 class MySQLDriver extends Parser implements DriverInterface {
42
43
44
45 private $resource;
46
47
48 private $unbuffered;
49
50
51 private $affectedRows;
52
53
54 55 56 57
58 public function __construct(BaseStatement $statement = null){
59 if(!extension_loaded("mysql"))
60 throw new DriverException("Cannot instantiate Neevo MySQL driver - PHP extension 'mysql' not loaded.");
61 if($statement instanceof BaseStatement)
62 parent::__construct($statement);
63 }
64
65
66 67 68 69 70
71 public function connect(array $config){
72
73
74 $defaults = array(
75 'resource' => null,
76 'charset' => 'utf8',
77 'username' => ini_get('mysql.default_user'),
78 'password' => ini_get('mysql.default_password'),
79 'host' => ini_get('mysql.default_host'),
80 'port' => ini_get('mysql.default_port'),
81 'persistent' => false,
82 'unbuffered' => false
83 );
84
85 $config += $defaults;
86 if(isset($config['port']))
87 $host = $config['host'] . ':' . $config['port'];
88 else
89 $host = $config['host'];
90
91
92 if(is_resource($config['resource']))
93 $connection = $config['resource'];
94 elseif($config['persistent'])
95 $connection = @mysql_pconnect($host, $config['username'], $config['password']);
96 else
97 $connection = @mysql_connect($host, $config['username'], $config['password']);
98
99 if(!is_resource($connection))
100 throw new DriverException("Connection to host '$host' failed.");
101
102
103 if(isset($config['database'])){
104 $db = mysql_select_db($config['database']);
105 if(!$db)
106 throw new DriverException("Could not select database '$config[database]'.");
107 }
108
109 $this->resource = $connection;
110
111
112 if(is_resource($connection)){
113 if(function_exists('mysql_set_charset'))
114 @mysql_set_charset($config['charset'], $connection);
115 else
116 $this->runQuery("SET NAMES " . $config['charset']);
117 }
118
119 $this->unbuffered = $config['unbuffered'];
120 }
121
122
123 124 125
126 public function closeConnection(){
127 @mysql_close($this->resource);
128 }
129
130
131 132 133 134 135
136 public function freeResultSet($resultSet){
137 return @mysql_free_result($resultSet);
138 }
139
140
141 142 143 144 145 146
147 public function runQuery($queryString){
148
149 $this->affectedRows = false;
150 if($this->unbuffered)
151 $result = @mysql_unbuffered_query($queryString, $this->resource);
152 else
153 $result = @mysql_query($queryString, $this->resource);
154
155 $error = str_replace('You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use', 'Syntax error', @mysql_error($this->resource));
156 if($error && $result === false)
157 throw new DriverException($error, @mysql_errno($this->resource), $queryString);
158
159 $this->affectedRows = @mysql_affected_rows($this->resource);
160 return $result;
161 }
162
163
164 165 166 167
168 public function beginTransaction($savepoint = null){
169 $this->runQuery($savepoint ? "SAVEPOINT $savepoint" : 'START TRANSACTION');
170 }
171
172
173 174 175 176
177 public function commit($savepoint = null){
178 $this->runQuery($savepoint ? "RELEASE SAVEPOINT $savepoint" : 'COMMIT');
179 }
180
181
182 183 184 185
186 public function rollback($savepoint = null){
187 $this->runQuery($savepoint ? "ROLLBACK TO SAVEPOINT $savepoint" : 'ROLLBACK');
188 }
189
190
191 192 193 194 195
196 public function fetch($resultSet){
197 return @mysql_fetch_assoc($resultSet);
198 }
199
200
201 202 203 204 205 206 207
208 public function seek($resultSet, $offset){
209 if($this->unbuffered){
210 throw new DriverException('Cannot seek on unbuffered result.');
211 }
212 return @mysql_data_seek($resultSet, $offset);
213 }
214
215
216 217 218 219
220 public function getInsertId(){
221 return @mysql_insert_id($this->resource);
222 }
223
224
225 226 227 228
229 public function randomizeOrder(BaseStatement $statement){
230 $statement->order('RAND()');
231 }
232
233
234 235 236 237 238 239
240 public function getNumRows($resultSet){
241 if($this->unbuffered)
242 throw new DriverException('Cannot count rows on unbuffered result.');
243 return @mysql_num_rows($resultSet);
244 }
245
246
247 248 249 250
251 public function getAffectedRows(){
252 return $this->affectedRows;
253 }
254
255
256 257 258 259 260 261 262
263 public function escape($value, $type){
264 switch($type){
265 case Manager::BOOL:
266 return $value ? 1 : 0;
267
268 case Manager::TEXT:
269 return "'" . mysql_real_escape_string($value, $this->resource) . "'";
270
271 case Manager::IDENTIFIER:
272 return str_replace('`*`', '*', '`' . str_replace('.', '`.`', str_replace('`', '``', $value)) . '`');
273
274 case Manager::BINARY:
275 return "_binary'" . mysql_real_escape_string($value, $this->resource) . "'";
276
277 case Manager::DATETIME:
278 return ($value instanceof DateTime) ? $value->format("'Y-m-d H:i:s'") : date("'Y-m-d H:i:s'", $value);
279
280 default:
281 throw new InvalidArgumentException('Unsupported data type.');
282 break;
283 }
284 }
285
286
287 288 289 290 291 292 293
294 public function unescape($value, $type){
295 if($type === Manager::BINARY)
296 return $value;
297 throw new InvalidArgumentException('Unsupported data type.');
298 }
299
300
301 302 303 304 305
306 public function getPrimaryKey($table){
307 $key = '';
308 $q = $this->runQuery('SHOW FULL COLUMNS FROM ' . $table);
309 while($col = $this->fetch($q)){
310 if(strtolower($col['Key']) === 'pri' && $key === '')
311 $key = $col['Field'];
312 }
313 return $key;
314 }
315
316
317 318 319 320 321 322
323 public function getColumnTypes($resultSet, $table){
324 $cols = array();
325 $count = mysql_num_fields($resultSet);
326 for($i = 0; $i < $count; $i++){
327 $field = mysql_fetch_field($resultSet, $i);
328 $cols[$field->name] = $field->type;
329 }
330 return $cols;
331 }
332
333
334 335 336 337
338 protected function parseUpdateStmt(){
339 $sql = parent::parseUpdateStmt();
340 return $this->applyLimit($sql . $this->clauses[3]);
341 }
342
343
344 345 346 347
348 protected function parseDeleteStmt(){
349 $sql = parent::parseDeleteStmt();
350 return $this->applyLimit($sql . $this->clauses[3]);
351 }
352
353
354 }
355