1: <?php
2:
3: /**
4: * This file contains the database driver handler class.
5: *
6: * @package Core
7: * @subpackage Database
8: * @author Dominik Ziegler
9: * @copyright four for business AG <www.4fb.de>
10: * @license http://www.contenido.org/license/LIZENZ.txt
11: * @link http://www.4fb.de
12: * @link http://www.contenido.org
13: */
14:
15: defined('CON_FRAMEWORK') || die('Illegal call: Missing framework initialization - request aborted.');
16:
17: /**
18: * This class contains functions for database driver handling in CONTENIDO.
19: *
20: * @package Core
21: * @subpackage Database
22: */
23: abstract class cDbDriverHandler {
24:
25: /**
26: *
27: * @var string
28: */
29: const HALT_YES = 'yes';
30:
31: /**
32: *
33: * @var string
34: */
35: const HALT_NO = 'no';
36:
37: /**
38: *
39: * @var string
40: */
41: const HALT_REPORT = 'report';
42:
43: /**
44: *
45: * @var string
46: */
47: const FETCH_NUMERIC = 'numeric';
48:
49: /**
50: *
51: * @var string
52: */
53: const FETCH_ASSOC = 'assoc';
54:
55: /**
56: *
57: * @var string
58: */
59: const FETCH_BOTH = 'both';
60:
61: /**
62: * Loader database driver.
63: *
64: * @var cDbDriverAbstract
65: */
66: protected $_driver = NULL;
67:
68: /**
69: * Driver type
70: *
71: * @var string
72: */
73: protected $_driverType = '';
74:
75: /**
76: * Default database connection for all instances
77: *
78: * @var array
79: */
80: protected static $_defaultDbCfg = array();
81:
82: /**
83: * Assoziative list of database connections
84: * @var array
85: */
86: protected static $_connectionCache = array();
87:
88: /**
89: * Assoziative list of database tables metadata
90: * @var array
91: */
92: protected static $_metaCache = array();
93:
94: /**
95: * Database connection configuration for current instance
96: *
97: * @var array
98: */
99: protected $_dbCfg = array();
100:
101: /**
102: * Halt status during occured errors.
103: * Feasible values are
104: * - "yes" (halt with message)
105: * - "no" (ignore errors quietly)
106: * - "report" (ignore errror, but spit a warning)
107: *
108: * @var string
109: */
110: protected $_haltBehaviour = 'no';
111:
112: /**
113: * Text to prepend to the halt message
114: *
115: * @var string
116: */
117: protected $_haltMsgPrefix = '';
118:
119: /**
120: * Profile data array
121: *
122: * @var array
123: */
124: protected static $_profileData = array();
125:
126: /**
127: * Constructor, sets passed options and connects to the DBMS, if not done
128: * before.
129: *
130: * Uses default connection settings, passed $options['connection'] settings
131: * will overwrite connection settings for current instance.
132: *
133: * @param array $options [optional]
134: * Assoziative options as follows:
135: * - $options['haltBehavior'] (string) Optional, halt behavior on
136: * occured errors
137: * - $options['haltMsgPrefix'] (string) Optional, Text to prepend to
138: * the halt message
139: * - $options['enableProfiling'] (bool) Optional, flag to enable
140: * profiling
141: * - $options['connection'] (array) Optional, assoziative connection
142: * settings
143: * - $options['connection']['host'] (string) Hostname or ip
144: * - $options['connection']['database'] (string) Database name
145: * - $options['connection']['user'] (string) User name
146: * - $options['connection']['password'] (string) User password
147: * @throws cDbException
148: */
149: public function __construct($options = array()) {
150: // use default connection configuration, but overwrite it by passed
151: // options
152: $this->_dbCfg = array_merge(self::$_defaultDbCfg, $options);
153:
154: // in case we do not have any configuration for database, try to load it from configuration
155: if (count($this->_dbCfg) == 0) {
156: $cfg = cRegistry::getConfig();
157: if (isset($cfg['db']) && count($cfg['db']) > 0) {
158: $this->_dbCfg = $cfg['db'];
159: } else {
160: throw new cDbException("Unable to establish a database connection without options!");
161: }
162: }
163:
164: if (isset($this->_dbCfg['haltBehavior'])) {
165: switch ($this->_dbCfg['haltBehavior']) {
166: case self::HALT_YES:
167: $this->_haltBehaviour = self::HALT_YES;
168: break;
169: case self::HALT_NO:
170: $this->_haltBehaviour = self::HALT_NO;
171: break;
172: case self::HALT_REPORT:
173: $this->_haltBehaviour = self::HALT_REPORT;
174: break;
175: }
176: }
177:
178: if (isset($this->_dbCfg['haltMsgPrefix']) && is_string($this->_dbCfg['haltMsgPrefix'])) {
179: $this->_haltMsgPrefix = $this->_dbCfg['haltMsgPrefix'];
180: }
181:
182: $cfg = cRegistry::getConfig();
183: $this->_driverType = $cfg['database_extension'];
184:
185: $this->loadDriver();
186:
187: try {
188: if ($this->connect() == NULL) {
189: $this->setErrorNumber(1);
190: $this->setErrorMessage("Could not connect to database");
191:
192: throw new cDbException($this->getErrorMessage());
193: }
194: } catch (Exception $e) {
195: throw new cDbException($e->getMessage());
196: }
197: }
198:
199: /**
200: * Checks if profiling was enabled via configuration.
201: *
202: * @return bool
203: */
204: public function isProfilingEnabled() {
205: return (bool) $this->_dbCfg['enableProfiling'];
206: }
207:
208: /**
209: * Returns the halt behaviour setting.
210: *
211: * @return string
212: */
213: public function getHaltBehaviour() {
214: return $this->_haltBehaviour;
215: }
216:
217: /**
218: * Loads the database driver and checks its base functionality.
219: *
220: * @throws cDbException
221: */
222: public function loadDriver() {
223: if ($this->_driver != NULL) {
224: return;
225: }
226:
227: $classNameSuffix = ucfirst($this->_driverType);
228:
229: $driverName = 'cDbDriver' . $classNameSuffix;
230:
231: if (class_exists($driverName) === false) {
232: throw new cDbException("Database driver was not found.");
233: }
234:
235: $this->_driver = new $driverName($this->_dbCfg);
236:
237: if (($this->getDriver() instanceof cDbDriverAbstract) === false) {
238: $this->_driver = NULL;
239: throw new cDbException("Database driver must extend cDbDriverAbstract");
240: }
241:
242: $this->getDriver()->setHandler($this);
243:
244: if ($this->getDriver()->check() === false) {
245: throw new cDbException("Database driver check failed.");
246: }
247: }
248:
249: /**
250: * Returns the database driver instance.
251: *
252: * @return cDbDriverAbstract
253: */
254: public function getDriver() {
255: return $this->_driver;
256: }
257:
258: /**
259: * Setter for default database configuration, the connection values.
260: *
261: * @param array $defaultDbCfg
262: */
263: public static function setDefaultConfiguration(array $defaultDbCfg) {
264: self::$_defaultDbCfg = $defaultDbCfg;
265: }
266:
267: /**
268: * Returns connection from connection cache
269: *
270: * @param mixed $data
271: * Connection data array or variable
272: * @return mixed
273: * Either The connection (object, resource, integer) or NULL
274: */
275: protected function _getConnection($data) {
276: $hash = md5($this->_driverType . '-' . (is_array($data) ? implode('-', $data) : (string)$data));
277:
278: return (isset(self::$_connectionCache[$hash])) ? self::$_connectionCache[$hash] : NULL;
279: }
280:
281: /**
282: * Stores connection in connection cache
283: *
284: * @param mixed $data
285: * Connection data array
286: * @param mixed $connection
287: * The connection to store in cache
288: */
289: protected function _setConnection($data, $connection) {
290: $hash = md5($this->_driverType . '-' . (is_array($data) ? implode('-', $data) : (string)$data));
291: self::$_connectionCache[$hash] = $connection;
292: }
293:
294: /**
295: * Removes connection from cache
296: *
297: * @param mixed $connection
298: * The connection to remove in cache
299: */
300: protected function _removeConnection($connection) {
301: foreach (self::$_connectionCache as $hash => $res) {
302: if ($res == $connection) {
303: unset(self::$_connectionCache[$hash]);
304:
305: return;
306: }
307: }
308: }
309:
310: /**
311: * Adds a entry to the profile data.
312: *
313: * @param float $timeStart
314: * @param float $timeEnd
315: * @param string $statement
316: */
317: protected static function _addProfileData($timeStart, $timeEnd, $statement) {
318: self::$_profileData[] = array(
319: 'time' => $timeEnd - $timeStart, 'query' => $statement
320: );
321: }
322:
323: /**
324: * Returns collected profile data.
325: *
326: * @return array
327: * Profile data array like:
328: * - $arr[$i]['time'] (float) Elapsed time to execute the query
329: * - $arr[$i]['query'] (string) The query itself
330: */
331: public static function getProfileData() {
332: return self::$_profileData;
333: }
334:
335: /**
336: * Prepares the statement for execution and returns it back.
337: * Accepts multiple parameter, where the first parameter should be the query
338: * and any additional parameter should be the values to replace in format
339: * definitions.
340: * As an alternative the second parameter cound be also a indexed array with
341: * values to replace in format definitions.
342: *
343: * Other option is to call this function with the statement containing named
344: * parameter
345: * and the second parameter as a assoziative array with key/value pairs to
346: * set in statement.
347: *
348: * Examples:
349: * <pre>
350: * // multiple parameter
351: * $sql = $obj->prepare('SELECT * FROM `%s` WHERE id = %d', 'tablename',
352: * 123);
353: *
354: * // 2 parameter where the first is the statement with formatting signs and
355: * the second the entries array
356: * $sql = $obj->prepare('SELECT * FROM `%s` WHERE id = %d',
357: * array('tablename', 123));
358: *
359: * // 2 parameter where the first is the statement with named parameter and
360: * the second the assoziative entries array
361: * $sql = $obj->prepare('SELECT * FROM `:mytab` WHERE id = :myid',
362: * array('mytab' => 'tablename', 'myid' => 123));
363: * </pre>
364: *
365: * Accepts additional unlimited parameter, where the parameter will be
366: * replaced against formatting sign in query.
367: *
368: * @param string $statement
369: * The sql statement to prepare.
370: * @return string
371: * The prepared sql statement
372: * @throws Exception
373: * If statement is empty or function is called with less than 2 parameters
374: */
375: public function prepare($statement) {
376: // No empty queries
377: if (empty($statement)) {
378: throw new cDbException('Empty statement!');
379: }
380:
381: $arguments = func_get_args();
382: if (count($arguments) <= 1) {
383: throw new cDbException('Wrong number of parameter!');
384: }
385:
386: array_shift($arguments);
387: $statement = $this->_prepareStatement($statement, $arguments);
388:
389: return $statement;
390: }
391:
392: /**
393: * Prepares the passed statement.
394: *
395: * @param string $statement
396: * @param array $arguments
397: *
398: * @return string
399: */
400: protected function _prepareStatement($statement, array $arguments) {
401: if (count($arguments) == 1 && is_array($arguments[0])) {
402: $arguments = $arguments[0];
403: if (count(array_filter(array_keys($arguments), 'is_string')) > 0) {
404: // we have at least one key being string, it is an assoc array
405: $statement = $this->_prepareStatementA($statement, $arguments);
406: } else {
407: // it is an indexed array
408: $statement = $this->_prepareStatementF($statement, $arguments);
409: }
410: } else {
411: $statement = $this->_prepareStatementF($statement, $arguments);
412: }
413:
414: return $statement;
415: }
416:
417: /**
418: * Prepares a statement with parameter for execution.
419: *
420: * Examples:
421: * <pre>
422: * $obj->_prepareStatementF('SELECT * FROM `%s` WHERE id = %d', 'tablename',
423: * 123);
424: * $obj->_prepareStatementF('SELECT * FROM `%s` WHERE id = %d AND user =
425: * %d', 'tablename', 123, 3);
426: * </pre>
427: *
428: * @param string $statement
429: * @param array $arguments
430: * Arguments array containing the query with formatting signs and
431: * the entries.
432: *
433: * @return string
434: */
435: protected function _prepareStatementF($statement, array $arguments) {
436: if (count($arguments) > 0) {
437: $arguments = array_map(array(
438: $this, 'escape'
439: ), $arguments);
440: array_unshift($arguments, $statement);
441: $statement = call_user_func_array('sprintf', $arguments);
442: }
443:
444: return $statement;
445: }
446:
447: /**
448: * Prepares a statement with named parameter for execution.
449: *
450: * Examples:
451: * <pre>
452: * // named parameter and assoziative entries array
453: * $sql = $obj->_prepareStatementA('SELECT * FROM `:mytab` WHERE id =
454: * :myid', array('mytab' => 'tablename', 'myid' => 123));
455: * $sql = $obj->_prepareStatementA('SELECT * FROM `:mytab` WHERE id = :myid
456: * AND user = :myuser', array('mytab' => 'tablename', 'myid' => 123,
457: * 'myuser' => 3));
458: * </pre>
459: *
460: * @param string $statement
461: * @param array $arguments
462: * Arguments array containing the query with named parameter and
463: * assoziative entries array
464: *
465: * @return string
466: */
467: protected function _prepareStatementA($statement, array $arguments) {
468: if (count($arguments) > 0) {
469: foreach ($arguments as $key => $value) {
470: $param = ':' . $key;
471: if (cSecurity::isInteger($value)) {
472: $statement = preg_replace('/' . $param . '/', cSecurity::toInteger($value), $statement);
473: $statement = preg_replace('/\'' . $param . '\'/', '\'' . cSecurity::toInteger($value) . '\'', $statement);
474: } else {
475: $param = cSecurity::toString($param);
476: $statement = preg_replace('/' . $param . '/', cSecurity::escapeString($value), $statement);
477: $statement = preg_replace('/\'' . $param . '\'/', '\'' . cSecurity::escapeString($value) . '\'', $statement);
478: $statement = preg_replace('/`' . $param . '`/', '`' . cSecurity::escapeString($value) . '`', $statement);
479: }
480: }
481: }
482:
483: return $statement;
484: }
485:
486: /**
487: * Establishes a connection to the database server.
488: *
489: * @return object|resource|int|NULL
490: * value depends on used driver and is NULL in case of an error.
491: */
492: public function connect() {
493: if (isset($this->_dbCfg['connection']) && $this->_linkId = $this->_getConnection($this->_dbCfg['connection'])) {
494: return $this->_linkId;
495: } else {
496: if ($this->_linkId = $this->getDriver()->connect()) {
497: $this->_setConnection($this->_dbCfg['connection'], $this->_linkId);
498:
499: return $this->_linkId;
500: }
501: }
502:
503: return NULL;
504: }
505:
506: /**
507: * Builds and executes a insert query.
508: * String values in passed aFields
509: * parameter will be escaped automatically.
510: *
511: * Example:
512: * <pre>
513: * $db = cRegistry::getDb();
514: * $fields = array(
515: * 'idcatart' => $idcatart,
516: * 'idlang' => $lang,
517: * 'idclient' => $client,
518: * 'code' => "<html>... code n' fun ...</html>",
519: * );
520: * $result = $db->insert($cfg['tab']['code'], $fields);
521: * </pre>
522: *
523: * @param string $tableName
524: * The table name
525: * @param array $fields
526: * Assoziative array of fields to insert
527: * @return bool
528: */
529: public function insert($tableName, array $fields) {
530: $statement = $this->buildInsert($tableName, $fields);
531:
532: return $this->query($statement);
533: }
534:
535: /**
536: * Builds and returns a insert query.
537: * String values in passed fields
538: * parameter will be escaped automatically.
539: *
540: * Example:
541: * <pre>
542: * $db = cRegistry::getDb();
543: * $fields = array(
544: * 'idcode' => $idcode,
545: * 'idcatart' => $idcatart,
546: * 'idlang' => $lang,
547: * 'idclient' => $client,
548: * 'code' => "<html>... code n' fun ...</html>",
549: * );
550: * $statement = $db->buildInsert($cfg['tab']['code'], $fields);
551: * $db->query($statement);
552: * </pre>
553: *
554: * @param string $tableName
555: * The table name
556: * @param array $fields
557: * Assoziative array of fields to insert
558: *
559: * @return string
560: */
561: public function buildInsert($tableName, array $fields) {
562: return $this->getDriver()->buildInsert($tableName, $fields);
563: }
564:
565: /**
566: * Builds and executes a update query.
567: * String values in passed fields
568: * and whereClauses parameter will be escaped automatically.
569: *
570: * Example:
571: * <pre>
572: * $db = cRegistry::getDb();
573: * $fields = array('code' => "<html>... some new code n' fun ...</html>");
574: * $whereClauses = array('idcode' => 123);
575: * $result = $db->update($cfg['tab']['code'], $fields, $whereClauses);
576: * </pre>
577: *
578: * @param string $tableName
579: * The table name
580: * @param array $fields
581: * Assoziative array of fields to update
582: * @param array $whereClauses
583: * Assoziative array of field in where clause.
584: * Multiple entries will be concatenated with AND
585: * @return bool
586: */
587: public function update($tableName, array $fields, array $whereClauses) {
588: $statement = $this->buildUpdate($tableName, $fields, $whereClauses);
589:
590: return $this->query($statement);
591: }
592:
593: /**
594: * Builds and returns a update query.
595: * String values in passed aFields
596: * and aWhere parameter will be escaped automatically.
597: *
598: * Example:
599: * <pre>
600: * $db = cRegistry::getDb();
601: * $fields = array('code' => "<html>... some new code n' fun ...</html>");
602: * $whereClauses = array('idcode' => 123);
603: * $statement = $db->buildUpdate($cfg['tab']['code'], $fields,
604: * $whereClauses);
605: * $db->query($statement);
606: * </pre>
607: *
608: * @param string $tableName
609: * The table name
610: * @param array $fields
611: * Assoziative array of fields to update
612: * @param array $whereClauses
613: * Assoziative array of field in where clause.
614: * Multiple entries will be concatenated with AND
615: * @return string
616: */
617: public function buildUpdate($tableName, array $fields, array $whereClauses) {
618: return $this->getDriver()->buildUpdate($tableName, $fields, $whereClauses);
619: }
620:
621: /**
622: * Executes the statement.
623: * If called with one parameter, it executes the statement directly.
624: *
625: * Accepts multiple parameter, where the first parameter should be the query
626: * and any additional parameter should be the values to replace in format
627: * definitions.
628: * As an alternative the second parameter cound be also a indexed array with
629: * values to replace in format definitions.
630: *
631: * Other option is to call this function with the statement containing named
632: * parameter
633: * and the second parameter as a assoziative array with key/value pairs to
634: * set in statement.
635: *
636: * Examples:
637: * <pre>
638: * // call with one parameter
639: * $obj->query('SELECT * FROM `tablename` WHERE id = 123');
640: *
641: * // call with multiple parameter
642: * $obj->query('SELECT * FROM `%s` WHERE id = %d', 'tablename', 123);
643: *
644: * // 2 parameter where the first is the statement with formatting signs and
645: * the second the entries array
646: * $obj->query('SELECT * FROM `%s` WHERE id = %d', array('tablename', 123));
647: *
648: * // 2 parameter where the first is the statement with named parameter and
649: * the second the assoziative entries array
650: * $obj->query('SELECT * FROM `:mytab` WHERE id = :myid', array('mytab' =>
651: * 'tablename', 'myid' => 123));
652: * </pre>
653: *
654: * Accepts additional unlimited parameter, where the parameter will be
655: * replaced against formatting sign in query.
656: *
657: * @param string $statement
658: * The SQL statement to execute.
659: * @return resource|int|object|bool
660: * database driver, false on error
661: */
662: public function query($statement) {
663: // No empty queries, please, since PHP4 chokes on them
664: if ($statement == '') {
665: // The empty query string is passed on from the constructor, when
666: // calling
667: // the class without a query, e.g. in situations '$db = new
668: // DB_Sql_Subclass;'
669: return false;
670: }
671:
672: $arguments = func_get_args();
673: if (count($arguments) > 1) {
674: array_shift($arguments);
675: $statement = $this->_prepareStatement($statement, $arguments);
676: }
677:
678: if (!$this->connect()) {
679: return false;
680: }
681:
682: // new query, discard previous result
683: if ($this->getQueryId()) {
684: $this->free();
685: }
686:
687: if ($this->isProfilingEnabled() === true) {
688: $timeStart = microtime(true);
689: }
690:
691: $this->getDriver()->query($statement);
692:
693: if ($this->isProfilingEnabled() === true) {
694: $timeEnd = microtime(true);
695: $this->_addProfileData($timeStart, $timeEnd, $statement);
696: }
697:
698: if (!$this->getQueryId()) {
699: $this->halt($statement);
700: }
701:
702: // Will return nada if it fails. That's fine.
703: return $this->getQueryId();
704: }
705:
706: /**
707: * Fetches the next record set from result set
708: *
709: * @return bool
710: */
711: public function nextRecord() {
712: if (!$this->getQueryId()) {
713: $currentModule = cRegistry::getCurrentModuleId();
714: if ($currentModule > 0) {
715: $this->halt('next_record called with no query pending in Module ID ' . $currentModule . '.');
716: } else {
717: $this->halt('next_record called with no query pending.');
718: }
719:
720: return false;
721: }
722:
723: return $this->getDriver()->nextRecord();
724: }
725:
726: /**
727: * This method returns the current result set as object or NULL if no result
728: * set is left.
729: * If optional param $className is set, the result object is an instance of
730: * class $className.
731: *
732: * @param string $className [optional]
733: * @return object
734: */
735: public function getResultObject($className = NULL) {
736: return $this->getDriver()->getResultObject($className);
737: }
738:
739: /**
740: * Returns number of affected rows from last executed query (update, delete)
741: *
742: * @return int
743: * Number of affected rows
744: */
745: public function affectedRows() {
746: return $this->getDriver()->affectedRows();
747: }
748:
749: /**
750: * Returns the number of rows from last executed select query.
751: *
752: * @return int
753: * The number of rows from last select query result
754: */
755: public function numRows() {
756: return $this->getDriver()->numRows();
757: }
758:
759: /**
760: * Returns the number of fields (columns) from current record set
761: *
762: * @return int
763: * Number of fields
764: */
765: public function numFields() {
766: return $this->getDriver()->numFields();
767: }
768:
769: /**
770: * Discard the query result
771: *
772: * @return int
773: */
774: public function free() {
775: return $this->getDriver()->free();
776: }
777:
778: /**
779: * Escape string for using in SQL-Statement.
780: *
781: * @param string $string
782: * The string to escape
783: * @return string
784: * Escaped string
785: */
786: public function escape($string) {
787: if (!$this->getLinkId()) {
788: $this->connect();
789: }
790:
791: return $this->getDriver()->escape($string);
792: }
793:
794: /**
795: * Moves the cursor (position inside current result sets).
796: *
797: * @param int $iPos
798: * The positon to move to inside the current result set
799: * @return int
800: */
801: public function seek($pos) {
802: $status = $this->getDriver()->seek($pos);
803: if ($status == 0) {
804: $this->halt("seek($pos) failed: result has " . $this->numRows() . " rows.");
805: }
806:
807: return $status;
808: }
809:
810: /**
811: * Get last inserted id of given table name
812: *
813: * @return int
814: * NULL id of table
815: */
816: public function getLastInsertedId() {
817: $lastId = NULL;
818:
819: $this->query('SELECT LAST_INSERT_ID() as last_id');
820: if ($this->nextRecord()) {
821: $lastId = $this->f('last_id');
822: }
823:
824: return $lastId;
825: }
826:
827: /**
828: * Parses te table structure and generates a metadata from it.
829: *
830: * @param string $tableName [optional]
831: * The table to get metadata or empty string to retrieve metadata
832: * of all tables
833: * @param bool $full [optional]
834: * Flag to load full metadata
835: * @return array
836: * Depends on used database and on parameter $full
837: */
838: public function getMetaData($tableName = '', $full = false) {
839: $databaseName = '';
840: $key = (string)$databaseName . '_' . $tableName . '_' . (($full) ? '1' : '0');
841:
842: if (!isset(self::$_metaCache[$key])) {
843: // get meta data
844: self::$_metaCache[$key] = $this->getDriver()->getMetaData($tableName, $full);
845: }
846:
847: return self::$_metaCache[$key];
848: }
849:
850: /**
851: * Returns names of existing tables.
852: *
853: * @return array|NULL
854: * array containing assoziative table data as follows or NULL:
855: * - $info[$i]['table_name']
856: * - $info[$i]['tablespace_name']
857: * - $info[$i]['database']
858: */
859: public function getTableNames() {
860: if (!$this->connect()) {
861: return NULL;
862: }
863:
864: return $this->getDriver()->getTableNames();
865: }
866:
867: /**
868: * Returns information about DB server.
869: * The return value depends always on
870: * used DBMS.
871: *
872: * @return array|NULL
873: * array as follows or NULL:
874: * - $arr['description'] (string) Optional, server description
875: * - $arr['version'] (string) Optional, server version
876: */
877: public function getServerInfo() {
878: if (!$this->connect()) {
879: return NULL;
880: }
881:
882: return $this->getDriver()->getServerInfo();
883: }
884:
885: /**
886: * Closes the connection and frees the query id.
887: */
888: public function disconnect() {
889: $linkId = $this->getLinkId();
890:
891: if (is_resource($linkId)) {
892: $this->getDriver()->disconnect();
893: $this->_removeConnection($linkId);
894: }
895:
896: $this->setLinkId(0);
897: $this->setQueryId(0);
898: }
899:
900: /**
901: * Returns the desired field value from current record set.
902: *
903: * @param mixed $name
904: * The field name or index position
905: * @param mixed $default [optional]
906: * The default value to return
907: * @return mixed
908: * The value of field
909: */
910: public function f($name, $default = NULL) {
911: $record = $this->getRecord();
912:
913: return (isset($record[$name])) ? $record[$name] : $default;
914: }
915:
916: /**
917: * Returns current record set as a associative and/or indexed array.
918: *
919: * @param string $fetchMode [optional]
920: * One of cDbDriverHandler::FETCH_* constants
921: * @return array
922: */
923: public function toArray($fetchMode = self::FETCH_ASSOC) {
924: switch ($fetchMode) {
925: case self::FETCH_NUMERIC:
926: case self::FETCH_ASSOC:
927: case self::FETCH_BOTH:
928: // donut
929: break;
930: default:
931: $fetchMode = self::FETCH_ASSOC;
932: break;
933: }
934:
935: $result = array();
936: if (is_array($this->getRecord())) {
937: foreach ($this->getRecord() as $key => $value) {
938: if ($fetchMode == self::FETCH_ASSOC && !is_numeric($key)) {
939: $result[$key] = $value;
940: } elseif ($fetchMode == self::FETCH_NUMERIC && is_numeric($key)) {
941: $result[$key] = $value;
942: } elseif ($fetchMode == self::FETCH_BOTH) {
943: $result[$key] = $value;
944: }
945: }
946: }
947:
948: return $result;
949: }
950:
951: /**
952: * Returns current record set as a object
953: *
954: * @return stdClass
955: */
956: public function toObject() {
957: return (object) $this->toArray(self::FETCH_ASSOC);
958: }
959:
960: /**
961: * Error handling
962: *
963: * Error handler function, delegates passed message to the function
964: * reportHalt() if property
965: * $this->_haltBehaviour is not set to self::HALT_REPORT.
966: *
967: * Terminates further script execution if $this->_haltBehaviour is set to
968: * self::HALT_YES
969: *
970: * @param string $message
971: * The message to use for error handling
972: * @throws cDbException
973: */
974: public function halt($message) {
975: if ($this->_haltBehaviour == self::HALT_REPORT) {
976: $this->reportHalt($this->_haltMsgPrefix . $message);
977: }
978:
979: if ($this->_haltBehaviour == self::HALT_YES) {
980: throw new cDbException($message);
981: }
982: }
983:
984: /**
985: * Logs passed message, basically the last db error to the error log.
986: * Concatenates a detailed error message and invoke PHP's error_log()
987: * method.
988: *
989: * @param string $message
990: */
991: public function reportHalt($message) {
992: $errorNumber = $this->getErrorNumber();
993: $errorMessage = $this->getErrorMessage();
994:
995: if (!$errorMessage) {
996: $errorMessage = $this->getDriver()->getErrorMessage();
997: }
998:
999: if (!$errorNumber) {
1000: $errorNumber = $this->getDriver()->getErrorNumber();
1001: }
1002:
1003: $message = sprintf("Database failure: %s (%s) - %s\n", $errorNumber, $errorMessage, $message);
1004: cWarning(__FILE__, __LINE__, $message);
1005: }
1006:
1007: /**
1008: * Returns the number of rows from last executed select query.
1009: *
1010: * @see cDbDriverHandler::numRows
1011: * @return int
1012: * The number of rows from last select query result
1013: */
1014: public function num_rows() {
1015: return $this->numRows();
1016: }
1017:
1018: /**
1019: * Returns number of affected rows from last executed query (update, delete)
1020: *
1021: * @see cDbDriverHandler::affectedRows
1022: * @return int
1023: * Number of affected rows
1024: */
1025: public function affected_rows() {
1026: return $this->affectedRows();
1027: }
1028:
1029: /**
1030: * Returns the number of fields (columns) from current record set
1031: *
1032: * @see cDbDriverHandler::numFields
1033: * @return int
1034: * Number of fields
1035: */
1036: public function num_fields() {
1037: return $this->numFields();
1038: }
1039:
1040: /**
1041: * Fetches the next record set from result set
1042: *
1043: * @see cDbDriverHandler::nextRecord
1044: * @return bool
1045: */
1046: public function next_record() {
1047: return $this->nextRecord();
1048: }
1049: }
1050: