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