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