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