OSDN Git Service

MERGE: リビジョン1873〜1893。skinnable-masterのマージ
[nucleus-jp/nucleus-next.git] / nucleus / libs / sql / MYSQLPDO.php
1 <?php
2 /*
3  * Nucleus: PHP/MySQL Weblog CMS (http://nucleuscms.org/)
4  * Copyright (C) 2012 The Nucleus Group
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  * (see nucleus/documentation/index.html#license for more info)
11  */
12 /**
13  * @license http://nucleuscms.org/license.txt GNU General Public License
14  * @copyright Copyright (C) 2012 The Nucleus Group
15  * @version $Id$
16  */
17
18 if ( !class_exists('PDO') )
19 {
20         /**
21          * Dummy constant of the PDO class
22          */
23         class PDO
24         {
25                 /* constant values */
26                 const PARAM_NULL = 0;
27                 const PARAM_INT = 1;
28                 const PARAM_STR = 2;
29                 const PARAM_LOB = 3;
30                 const PARAM_STMT = 4;
31                 const PARAM_BOOL = 5;
32                 const PARAM_INPUT_OUTPUT = 128; // orignal is undefined.
33                 const FETCH_LAZY = 1;
34                 const FETCH_ASSOC = 2;
35                 const FETCH_NUM = 3;
36                 const FETCH_BOTH = 4;
37                 const FETCH_OBJ = 5;
38                 const FETCH_BOUND = 6;
39                 const FETCH_COLUMN = 7;
40                 const FETCH_CLASS = 8;
41                 const FETCH_INTO = 9;
42                 const FETCH_FUNC = 10;
43                 const FETCH_NAMED = 11;
44                 const FETCH_KEY_PAIR = 12;
45                 const FETCH_GROUP = 65536;
46                 const FETCH_UNIQUE = 196608;
47                 const FETCH_CLASSTYPE = 262144;
48                 const FETCH_SERIALIZE = 524288;
49                 const FETCH_PROPS_LATE = 1048576;
50                 const ATTR_AUTOCOMMIT = 0;
51                 const ATTR_PREFETCH = 1;
52                 const ATTR_TIMEOUT = 2;
53                 const ATTR_ERRMODE = 3;
54                 const ATTR_SERVER_VERSION = 4;
55                 const ATTR_CLIENT_VERSION = 5;
56                 const ATTR_SERVER_INFO = 6;
57                 const ATTR_CONNECTION_STATUS = 7;
58                 const ATTR_CASE = 8;
59                 const ATTR_CURSOR_NAME = 9;
60                 const ATTR_CURSOR = 10;
61                 const ATTR_ORACLE_NULLS = 11;
62                 const ATTR_PERSISTENT = 12;
63                 const ATTR_STATEMENT_CLASS = 13;
64                 const ATTR_FETCH_TABLE_NAMES = 14;
65                 const ATTR_FETCH_CATALOG_NAMES = 15;
66                 const ATTR_DRIVER_NAME = 16;
67                 const ATTR_STRINGIFY_FETCHES = 17;
68                 const ATTR_MAX_COLUMN_LEN = 18;
69                 const ATTR_DEFAULT_FETCH_MODE = 19;
70                 const ATTR_EMULATE_PREPARES = 20;
71                 const ERRMODE_SILENT = 0;
72                 const ERRMODE_WARNING = 1;
73                 const ERRMODE_EXCEPTION = 2;
74                 const CASE_NATURAL = 0;
75                 const CASE_UPPER = 1;
76                 const CASE_LOWER = 2;
77                 const NULL_NATURAL = 0;
78                 const NULL_EMPTY_STRING = 1;
79                 const NULL_TO_STRING = 2;
80                 const FETCH_ORI_NEXT = 0;
81                 const FETCH_ORI_PRIOR = 1;
82                 const FETCH_ORI_FIRST = 2;
83                 const FETCH_ORI_LAST = 3;
84                 const FETCH_ORI_ABS = 4;
85                 const FETCH_ORI_REL = 5;
86                 const CURSOR_FWDONLY = 0;
87                 const CURSOR_SCROLL = 1;
88                 // from here orignal is undefined.
89                 const ERR_CANT_MAP = 0;
90                 const ERR_SYNTAX = 0;
91                 const ERR_CONSTRAINT = 0;
92                 const ERR_NOT_FOUND = 0;
93                 const ERR_ALREADY_EXISTS = 0;
94                 const ERR_NOT_IMPLEMENTED = 0;
95                 const ERR_MISMATCH = 0;
96                 const ERR_TRUNCATED = 0;
97                 const ERR_DISCONNECTED = 0;
98                 const ERR_NO_PERM = 0;
99                 // so far
100                 const ERR_NONE = '00000';
101                 const PARAM_EVT_ALLOC = 0;
102                 const PARAM_EVT_FREE = 1;
103                 const PARAM_EVT_EXEC_PRE = 2;
104                 const PARAM_EVT_EXEC_POST = 3;
105                 const PARAM_EVT_FETCH_PRE = 4;
106                 const PARAM_EVT_FETCH_POST = 5;
107                 const PARAM_EVT_NORMALIZE = 6;
108
109                 const MYSQL_ATTR_INIT_COMMAND = 1002;
110         }
111
112         /**
113          * PDOException class of dummy
114          */
115         class PDOException extends Exception
116         {}
117 }
118
119 /**
120  * MysqlPDO class that wraps the mysql_ or mysqli_ function like PDO class
121  */
122 class MysqlPDO
123 {
124         // Prefix function name
125         public static $handler;
126
127         private $dbcon;
128
129         /**
130          * Creates a PDO instance representing a connection to a MySQL database.
131          * @param string $dsn DSN
132          * @param string $username UserName
133          * @param string $password Password
134          * @param mixed $driver_options Options[optional]
135          * @throws PDOException Thrown when failed to connect to the database.
136          */
137         public function __construct($dsn, $username, $password, $driver_options = '')
138         {
139                 // select use function
140                 if ( function_exists('mysql_query') )
141                 {
142                         MysqlPDO::$handler = 'mysql_';
143                 }
144                 else if ( function_exists('mysqli_query') )
145                 {
146                         MysqlPDO::$handler = 'mysqli_';
147                 }
148                 else
149                 {
150                         throw new PDOException('Can not be found mysql_ or mysqli_ functions.', 'IM000');
151                 }
152
153                 if ( preg_match('/host=([^;]+)/', $dsn, $matches) )
154                 {
155                         $host = $matches[1];
156                 }
157                 else
158                 {
159                         throw new PDOException('Host has not been set.', '01000');
160                 }
161                 if ( preg_match('/port=([^;]+)/', $dsn, $matches) )
162                 {
163                         $host .= ':' . $matches[1];
164                 }
165
166                 // mysql connect
167                 $this->dbcon = @call_user_func(MysqlPDO::$handler . 'connect', $host, $username, $password);
168
169                 if ( $this->dbcon == FALSE )
170                 {
171                         throw new PDOException('Failed to connect to the server.', '01000');
172                 }
173
174                 // select database
175                 if ( preg_match('/dbname=([^;]+)/', $dsn, $matches) )
176                 {
177                         $dbname = $matches[1];
178                         if ( $dbname )
179                         {
180                                 if ( MysqlPDO::$handler == 'mysql_' )
181                                 {
182                                         call_user_func(MysqlPDO::$handler . 'select_db', $dbname, $this->dbcon);
183                                 }
184                                 else
185                                 {
186                                         call_user_func(MysqlPDO::$handler . 'select_db', $this->dbcon, $dbname);
187                                 }
188
189                                 // set use character
190                                 $charset = 'utf8';
191                                 if ( is_array($driver_options) && array_key_exists(PDO::MYSQL_ATTR_INIT_COMMAND, $driver_options) )
192                                 {
193                                         if ( preg_match('/SET\s+CHARACTER\s+SET\s+\'?([a-z0-9_-]+)\'?/', $driver_options[PDO::MYSQL_ATTR_INIT_COMMAND], $matches) )
194                                         {
195                                                 $charset = $matches[1];
196                                         }
197                                 }
198                                 $server_info = call_user_func(MysqlPDO::$handler . 'get_server_info', $this->dbcon);
199                                 $mysql_version = preg_replace('/^([0-9]{1,2})\.([0-9]{1,2})\.([0-9]{1,2})(.*)$/', '$1.$2.$3', $server_info);
200
201                                 if ( version_compare($mysql_version, '5.0.7', '>=') && function_exists(MysqlPDO::$handler . 'set_charset') )
202                                 {
203                                         call_user_func(MysqlPDO::$handler . 'set_charset', $charset);
204                                 }
205                                 else if ( version_compare($mysql_version, '5.0.7', '<') )
206                                 {
207                                         $this->exec("SET CHARACTER SET '{$charset}';");
208                                 }
209                                 else
210                                 {
211                                         $this->exec("SET NAMES '{$charset}';");
212                                 }
213                         }
214                 }
215         }
216
217         /**
218          * Close the connection to the MySQL server.
219          */
220         public function __destruct()
221         {
222                 if ( $this->dbcon )
223                 {
224                         @call_user_func(MysqlPDO::$handler . 'close', $this->dbcon);
225                 }
226         }
227
228         /**
229          * Not supported
230          */
231         public function beginTransaction()
232         {
233                 return FALSE;
234         }
235
236         /**
237          * Not supported
238          */
239         public function commit()
240         {
241                 return FALSE;
242         }
243
244         /**
245          * Fetch the SQLSTATE associated with the last operation.
246          * However, if successful '00000' Otherwise, the return to '01000'.
247          * @return string Error code
248          */
249         public function errorCode()
250         {
251                 $errno = call_user_func(MysqlPDO::$handler . 'errno', $this->dbcon);
252                 return $errno === 0 ? '00000' : '01000';
253         }
254
255         /**
256          * To get extended error information associated with the last operation.
257          * Element 0: if successful '00000' Otherwise, the return to '01000'
258          * Element 1: The return value of mysql_errno()
259          * Element 2: The return value of mysql_error()
260          * @return array Array of error information
261          */
262         public function errorInfo()
263         {
264                 $errno = call_user_func(MysqlPDO::$handler . 'errno', $this->dbcon);
265                 $error = call_user_func(MysqlPDO::$handler . 'error', $this->dbcon);
266                 return array($errno === 0 ? '00000' : '01000', $errno, $error);
267         }
268
269         public function exec($statement)
270         {
271                 $result = @call_user_func(MysqlPDO::$handler . 'query', $statement, $this->dbcon);
272                 if ( $result === TRUE )
273                 {
274                         return @call_user_func(MysqlPDO::$handler . 'affected_rows', $this->dbcon);
275                 }
276                 else if ( is_resource($result) )
277                 {
278                         return;
279                 }
280                 return FALSE;
281         }
282
283         public function getAttribute($attribute)
284         {
285                 switch ( $attribute )
286                 {
287                         case PDO::ATTR_SERVER_VERSION:
288                                 return call_user_func(MysqlPDO::$handler . 'get_server_info', $this->dbcon);
289                                 break;
290                         case PDO::ATTR_CLIENT_VERSION:
291                                 return call_user_func(MysqlPDO::$handler . 'get_client_info');
292                                 break;
293                         default:
294                                 return FALSE;
295                 }
296         }
297
298         public static function getAvailableDrivers()
299         {
300                 return array('mysql');
301         }
302
303         /**
304          * Not supported
305          */
306         public function inTransaction()
307         {
308                 return FALSE;
309         }
310
311         public function lastInsertId($name = null)
312         {
313                 return call_user_func(MysqlPDO::$handler . 'insert_id', $this->dbcon);
314         }
315
316         /**
317          * Not supported
318          */
319         public function prepare($statement, $driver_options = array())
320         {
321                 return FALSE;
322         }
323
324         public function query($statement)
325         {
326                 $result = @call_user_func(MysqlPDO::$handler . 'query', $statement, $this->dbcon);
327                 return ($result == FALSE) ? FALSE : new MysqlPDOStatement($statement, $result, $this->dbcon);
328         }
329
330         public function quote($string, $parameter_type = PDO::PARAM_STR)
331         {
332                 switch ( $parameter_type )
333                 {
334                         case PDO::PARAM_NULL:
335                                 return 'null';
336                         case PDO::PARAM_BOOL:
337                                 return $string ? '1' : '0';
338                         default:
339                                 if ( $parameter_type == PDO::PARAM_INT && is_numeric($string) )
340                                 {
341                                         return $string;
342                                 }
343                                 else
344                                 {
345                                         return '\'' . call_user_func(MysqlPDO::$handler . 'real_escape_string', $string) . '\'';
346                                 }
347                 }
348         }
349
350         /**
351          * Not supported
352          */
353         public function rollBack()
354         {
355                 return FALSE;
356         }
357
358         /**
359          * Not supported
360          */
361         public function setAttribute($attribute, $value)
362         {
363                 return FALSE;
364         }
365 }
366
367 /**
368  * MysqlPDOStatement class PDOStatement class like.
369  */
370 class MysqlPDOStatement implements Iterator
371 {
372         private $result;
373         private $dbcon;
374         private $_queryString = '';
375
376         private $def_fetch_mode = PDO::FETCH_BOTH;
377         private $def_col_num = 0;
378         private $def_class_name = 'stdClass';
379         private $def_ctorargs = null;
380         private $bind_object = null;
381
382         public function __get($name)
383         {
384                 if ( $name == 'queryString' )
385                 {
386                         return $this->_queryString;
387                 }
388         }
389
390         public function __construct($query, $result, $dbconnect = null)
391         {
392                 $this->dbcon = $dbconnect;
393                 $this->_queryString = $query;
394                 $this->result = $result;
395         }
396
397         public function __destruct()
398         {
399                 $this->result = null;
400         }
401
402         /**
403          * Not supported
404          */
405         public function bindColumn($column, &$param, $type, $maxlen, $driverdata)
406         {
407                 return FALSE;
408         }
409
410         /**
411          * Not supported
412          */
413         public function bindParam($parameter, &$variable, $data_type = PDO::PARAM_STR, $length, $driver_options)
414         {
415                 return FALSE;
416         }
417
418         /**
419          * Not supported
420          */
421         public function bindValue($parameter, $value, $data_type = PDO::PARAM_STR)
422         {
423                 return FALSE;
424         }
425
426         public function closeCursor()
427         {
428                 return call_user_func(MysqlPDO::$handler . 'free_result', $this->result);
429         }
430
431         public function columnCount()
432         {
433                 return call_user_func(MysqlPDO::$handler . 'num_fields', $this->result);
434         }
435
436         /**
437          * Not supported
438          */
439         public function debugDumpParams()
440         {
441                 return;
442         }
443
444         public function errorCode()
445         {
446                 $errno = call_user_func(MysqlPDO::$handler . 'errno', $this->dbcon);
447                 return $errno === 0 ? '00000' : '01000';
448         }
449
450         public function errorInfo()
451         {
452                 $errno = call_user_func(MysqlPDO::$handler . 'errno', $this->dbcon);
453                 $error = call_user_func(MysqlPDO::$handler . 'error', $this->dbcon);
454                 return array($errno === 0 ? '00000' : '01000', $errno, $error);
455         }
456
457         /**
458          * Not supported
459          */
460         public function execute($input_parameters)
461         {
462                 return FALSE;
463         }
464
465         public function fetch($fetch_style = PDO::ATTR_DEFAULT_FETCH_MODE, $cursor_orientation = PDO::FETCH_ORI_NEXT, $cursor_offset = 0)
466         {
467                 if ( !is_resource($this->result) || $cursor_orientation != PDO::FETCH_ORI_NEXT )
468                 {
469                         return FALSE;
470                 }
471                 
472                 if ( $fetch_style == PDO::ATTR_DEFAULT_FETCH_MODE )
473                 {
474                         $fetch_style = $this->def_fetch_mode;
475                 }
476
477                 switch ( $fetch_style )
478                 {
479                         case PDO::FETCH_ASSOC:
480                                 return @call_user_func(MysqlPDO::$handler . 'fetch_array', $this->result, MYSQL_ASSOC);
481                         case PDO::FETCH_BOTH:
482                                 return @call_user_func(MysqlPDO::$handler . 'fetch_array', $this->result, MYSQL_BOTH);
483                         case PDO::FETCH_NUM:
484                                 return @call_user_func(MysqlPDO::$handler . 'fetch_array', $this->result, MYSQL_NUM);
485                         case PDO::FETCH_OBJ:
486                                 return $this->fetchObject();
487                         case PDO::FETCH_CLASS:
488                                 return $this->fetchObject($this->def_class_name, $this->def_ctorargs);
489                         case PDO::FETCH_COLUMN:
490                                 return $this->fetchColumn($this->def_col_num);
491                         case PDO::FETCH_BOUND:
492                                 return FALSE; // Not supported
493                         case PDO::FETCH_INTO:
494                                 return FALSE; // Not supported
495                         case PDO::FETCH_LAZY:
496                                 return FALSE; // Not supported
497                         default:
498                                 return FALSE;
499                 }
500         }
501
502         public function fetchAll($fetch_style = PDO::ATTR_DEFAULT_FETCH_MODE, $fetch_argument = null, $ctor_args = array())
503         {
504                 if ( $fetch_style == PDO::ATTR_DEFAULT_FETCH_MODE )
505                 {
506                         $fetch_style = PDO::FETCH_BOTH;
507                 }
508                 
509                 $ret = array();
510                 if ( ($fetch_style & PDO::FETCH_COLUMN) != 0 )
511                 {
512                         if ( $fetch_style == PDO::FETCH_COLUMN )
513                         {
514                                 $column = $fetch_argument == null ? 0 : intval($fetch_argument);
515                                 while ( $row = $this->fetchColumn($column) )
516                                 {
517                                         $ret[] = $row;
518                                 }
519                         }
520                         elseif ( ($fetch_style & PDO::FETCH_UNIQUE) != 0 )
521                         {
522                                 return FALSE;
523                         }
524                         elseif ( ($fetch_style & PDO::FETCH_GROUP) != 0 )
525                         {
526                                 return FALSE;
527                         }
528                 }
529                 elseif ( $fetch_style == PDO::FETCH_CLASS )
530                 {
531                         while ( $row = $this->fetchObject($fetch_argument, $ctor_args) )
532                         {
533                                 $ret[] = $row;
534                         }
535                 }
536                 elseif ( $fetch_style == PDO::FETCH_FUNC )
537                 {
538                         while ( $row = $this->fetch(PDO::FETCH_ASSOC) )
539                         {
540                                 $ret[] = call_user_func_array($fetch_argument, array_values($row));
541                         }
542                 }
543                 else
544                 {
545                         while ( $row = $this->fetch($fetch_style) )
546                         {
547                                 $ret[] = $row;
548                         }
549                 }
550                 return $ret;
551         }
552
553         public function fetchColumn($column_number = 0)
554         {
555                 if ( $ret = $this->fetch(PDO::FETCH_NUM) )
556                 {
557                         return $ret[$column_number];
558                 }
559                 return FALSE;
560         }
561
562         public function fetchObject($class_name = 'stdClass', $ctor_args = null)
563         {
564                 if ( is_array($ctor_args) && !empty($ctor_args) )
565                 {
566                         return @call_user_func(MysqlPDO::$handler . 'fetch_object', $this->result, $class_name, $ctor_args);
567                 }
568                 else
569                 {
570                         return @call_user_func(MysqlPDO::$handler . 'fetch_object', $this->result, $class_name);
571                 }
572         }
573
574         public function getAttribute($attribute)
575         {
576                 switch ( $attribute )
577                 {
578                         default:
579                                 return FALSE;
580                 }
581         }
582
583         /**
584          * Not supported
585          */
586         public function getColumnMeta($column)
587         {
588                 $result = array();
589                 if ( MysqlPDO::$handler == 'mysql_' )
590                 {
591                         $result['name'] = @call_user_func(MysqlPDO::$handler . 'field_name', $this->result, $column);
592                 }
593                 return $result;
594         }
595
596         /**
597          * Not supported
598          */
599         public function nextRowset()
600         {
601                 return FALSE;
602         }
603
604         public function rowCount()
605         {
606                 return @call_user_func(MysqlPDO::$handler . 'affected_rows', $this->dbcon);
607         }
608
609         /**
610          * Not supported
611          */
612         public function setAttribute($attribute, $value)
613         {
614                 return FALSE;
615         }
616
617         public function setFetchMode($mode, &$mode_argument, $ctorargs)
618         {
619                 switch ( $mode )
620                 {
621                         case PDO::FETCH_COLUMN:
622                                 $this->def_col_num = $mode_argument;
623                                 break;
624                         case PDO::FETCH_CLASS:
625                                 $this->def_class_name = $mode_argument;
626                                 $this->def_ctorargs = $ctorargs;
627                                 break;
628                         case PDO::FETCH_INTO:
629                                 $this->bind_object = &$mode_argument;
630                                 return FALSE; // Not supported
631                         default:
632                                 $this->def_fetch_mode = $mode;
633                                 break;
634                 }
635                 return 1;
636         }
637
638         // Iterator
639         private $iterator_value;
640
641         function rewind()
642         {}
643
644         function next()
645         {}
646
647         function valid()
648         {
649                 return ($this->iterator_value = $this->fetch());
650         }
651
652         function current()
653         {
654                 return $this->iterator_value;
655         }
656
657         function key()
658         {
659                 return null;
660         }
661 }