1: <?php
2:
3: /**
4: * This file contains the MySQL database driver 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 interaction based on MySQL in CONTENIDO.
19: *
20: * Configurable via global $cfg['db']['connection'] configuration as follows:
21: * <pre>
22: * - host (string) Hostname or ip (with port, e.g. "example.com:3307")
23: * - database (string) Database name
24: * - user (string) User name
25: * - password (string) User password
26: * - charset (string) Optional, connection charset
27: * see http://php.net/manual/en/function.mysql-connect.php
28: * </pre>
29: *
30: * @package Core
31: * @subpackage Database
32: */
33: class cDbDriverMysql extends cDbDriverAbstract {
34:
35: /**
36: * Abstract method for checking database driver base functions.
37: * If this check fails, the database connection will not be established.
38: *
39: * @see cDbDriverAbstract::check()
40: * @return bool
41: */
42: public function check() {
43: return function_exists("mysql_connect");
44: }
45:
46: /**
47: * Connects to the database.
48: *
49: * @see cDbDriverAbstract::connect()
50: * @return object|resource|int|NULL
51: * value depends on used driver and is NULL in case of an error
52: * @throws cDbException
53: */
54: public function connect() {
55: if (isset($this->_dbCfg['connection'])) {
56: $connectConfig = $this->_dbCfg['connection'];
57: }
58: if (empty($connectConfig) || !isset($connectConfig['host']) || !isset($connectConfig['user']) || !isset($connectConfig['password'])) {
59: $this->_handler->halt('Database connection settings incomplete');
60:
61: return NULL;
62: }
63:
64: // establish connection, select database
65: $dbHandler = mysql_connect($connectConfig['host'], $connectConfig['user'], $connectConfig['password']);
66: if (!$dbHandler || !is_resource($dbHandler)) {
67: $this->_handler->halt('Error during establishing a connection with database.');
68:
69: return NULL;
70: }
71:
72: if (isset($connectConfig['database'])) {
73: if (!mysql_select_db($connectConfig['database'], $dbHandler)) {
74: $this->_handler->halt('Can not use database ' . $connectConfig['database']);
75:
76: return NULL;
77: } else {
78: //set connection charset
79: if (isset($connectConfig['charset']) && $connectConfig['charset'] != '') {
80: if (!mysql_set_charset($connectConfig['charset'], $dbHandler)) {
81: $this->_handler->halt('Could not set database charset to ' . $connectConfig['charset']);
82:
83: return NULL;
84: }
85: }
86: }
87: }
88:
89: return $dbHandler;
90: }
91:
92: /**
93: * Builds a insert query.
94: * String values in passed fields parameter will be escaped automatically.
95: *
96: * @see cDbDriverAbstract::buildInsert()
97: * @param string $tableName
98: * The table name
99: * @param array $fields
100: * Associative array of fields to insert
101: * @return string
102: * The INSERT SQL query
103: */
104: public function buildInsert($tableName, array $fields) {
105: $fieldList = '';
106: $valueList = '';
107:
108: foreach ($fields as $field => $value) {
109: $fieldList .= '`' . $field . '`, ';
110: if (is_int($value)) {
111: $valueList .= $value . ', ';
112: } else {
113: $valueList .= "'" . $this->escape($value) . "', ";
114: }
115: }
116:
117: $fieldList = cString::getPartOfString($fieldList, 0, -2);
118: $valueList = cString::getPartOfString($valueList, 0, -2);
119:
120: return sprintf('INSERT INTO `%s` (%s) VALUES (%s)', $tableName, $fieldList, $valueList);
121: }
122:
123: /**
124: * Builds a update query. String values in passed fields and whereClauses
125: * parameter will be escaped automatically.
126: *
127: * @see cDbDriverAbstract::buildUpdate()
128: * @param string $tableName
129: * The table name
130: * @param array $fields
131: * Assoziative array of fields to update
132: * @param array $whereClauses
133: * Assoziative array of field in where clause.
134: * Multiple entries will be concatenated with AND.
135: * @return string
136: * The UPDATE query
137: */
138: public function buildUpdate($tableName, array $fields, array $whereClauses) {
139: $updateList = '';
140: $whereList = '';
141:
142: foreach ($fields as $field => $value) {
143: $updateList .= '`' . $field . '`=';
144: if (is_int($value)) {
145: $updateList .= $value . ', ';
146: } else {
147: $updateList .= "'" . $this->escape($value) . "', ";
148: }
149: }
150:
151: foreach ($whereClauses as $field => $value) {
152: $whereList .= '`' . $field . '`=';
153: if (is_int($value)) {
154: $whereList .= $value . ' AND ';
155: } else {
156: $whereList .= "'" . $this->escape($value) . "' AND ";
157: }
158: }
159:
160: $updateList = cString::getPartOfString($updateList, 0, -2);
161: $whereList = cString::getPartOfString($whereList, 0, -5);
162:
163: return sprintf('UPDATE `%s` SET %s WHERE %s', $tableName, $updateList, $whereList);
164: }
165:
166: /**
167: * Executes the query.
168: *
169: * @see cDbDriverAbstract::query()
170: * @param string $query
171: * The query to execute
172: */
173: public function query($query) {
174: $linkId = $this->_handler->getLinkId();
175: $queryId = @mysql_query($query, $linkId);
176:
177: $this->_handler->setQueryId($queryId);
178: $this->_handler->setRow(0);
179: $this->_handler->setErrorNumber($this->getErrorNumber());
180: $this->_handler->setErrorMessage($this->getErrorMessage());
181: }
182:
183: /**
184: * Moves the result to the next record, if exists and returns the status of
185: * the movement
186: *
187: * @see cDbDriverAbstract::nextRecord()
188: * @return int
189: * Flag about move status 1 on success or 0
190: */
191: public function nextRecord() {
192: $queryId = $this->_handler->getQueryId();
193: $record = @mysql_fetch_array($queryId);
194:
195: $this->_handler->setRecord($record);
196: $this->_handler->incrementRow();
197: $this->_handler->setErrorNumber($this->getErrorNumber());
198: $this->_handler->setErrorMessage($this->getErrorMessage());
199:
200: return is_array($record);
201: }
202:
203: /**
204: * This method returns the current result set as object or NULL if no result
205: * set is left. If optional param $className is set, the result object is an
206: * instance of class $className.
207: *
208: * @see cDbDriverAbstract::getResultObject()
209: * @param string $className [optional]
210: * @return Ambigous <NULL, object, false>
211: */
212: public function getResultObject($className = NULL) {
213: $result = NULL;
214: $queryId = $this->_handler->getQueryId();
215:
216: if (is_resource($queryId)) {
217: if ($className == NULL) {
218: $result = mysql_fetch_object($queryId);
219: } else {
220: $result = mysql_fetch_object($queryId, $className);
221: }
222: }
223:
224: return $result;
225: }
226:
227: /**
228: * Returns number of affected rows from last executed query (update, delete)
229: *
230: * @see cDbDriverAbstract::affectedRows()
231: * @return int
232: * Number of affected rows
233: */
234: public function affectedRows() {
235: $linkId = $this->_handler->getLinkId();
236:
237: return ($linkId) ? mysql_affected_rows($linkId) : 0;
238: }
239:
240: /**
241: * Returns the number of rows from last executed select query.
242: *
243: * @see cDbDriverAbstract::numRows()
244: * @return int
245: * The number of rows from last select query result
246: */
247: public function numRows() {
248: $queryId = $this->_handler->getQueryId();
249:
250: return ($queryId) ? mysql_num_rows($queryId) : 0;
251: }
252:
253: /**
254: * Returns the number of fields (columns) from current record set
255: *
256: * @see cDbDriverAbstract::numFields()
257: * @return int
258: * Number of fields
259: */
260: public function numFields() {
261: $queryId = $this->_handler->getQueryId();
262:
263: return ($queryId) ? mysql_num_fields($queryId) : 0;
264: }
265:
266: /**
267: * Discard the query result
268: *
269: * @see cDbDriverAbstract::free()
270: */
271: public function free() {
272: @mysql_free_result($this->_handler->getQueryId());
273: $this->_handler->setQueryId(0);
274: }
275:
276: /**
277: * Escape string for using in SQL-Statement.
278: *
279: * @see cDbDriverAbstract::escape()
280: * @param string $string
281: * The string to escape
282: * @return string
283: * Escaped string
284: */
285: public function escape($string) {
286: $linkId = $this->_handler->getLinkId();
287:
288: return mysql_real_escape_string($string, $linkId);
289: }
290:
291: /**
292: * Moves the cursor (position inside current result sets).
293: *
294: * @see cDbDriverAbstract::seek()
295: * @param int $pos [optional]
296: * The positon to move to inside the current result set
297: * @return int
298: */
299: public function seek($pos = 0) {
300: $queryId = $this->_handler->getQueryId();
301:
302: $status = @mysql_data_seek($queryId, $pos);
303: if ($status) {
304: $this->_handler->setRow($pos);
305: } else {
306: return 0;
307: }
308:
309: return 1;
310: }
311:
312: /**
313: * Parses the table structure and generates metadata from it.
314: *
315: * Due to compatibility problems with table we changed the behavior
316: * of metadata(). Depending on $full, metadata returns the following values:
317: *
318: * - full is false (default):
319: * $result[]:
320: * [0]["table"] table name
321: * [0]["name"] field name
322: * [0]["type"] field type
323: * [0]["len"] field length
324: * [0]["flags"] field flags
325: *
326: * - full is true
327: * $result[]:
328: * ["num_fields"] number of metadata records
329: * [0]["table"] table name
330: * [0]["name"] field name
331: * [0]["type"] field type
332: * [0]["len"] field length
333: * [0]["flags"] field flags
334: * ["meta"][field name] index of field named "field name"
335: * This last one could be used if you have a field name, but no index.
336: * Test: if (isset($result['meta']['myfield'])) { ...
337: *
338: * @see cDbDriverAbstract::getMetaData()
339: *
340: * @param string $tableName
341: * The table to get metadata or empty string to retrieve metadata
342: * of all tables.
343: * @param bool $full [optional]
344: * Flag to load full metadata.
345: *
346: * @return array
347: * Depends on used database and on parameter $full
348: * @throws cDbException
349: */
350: public function getMetaData($tableName, $full = false) {
351: $res = array();
352:
353: $this->query(sprintf('SELECT * FROM `%s` LIMIT 1', $tableName));
354: $id = $this->_handler->getQueryId();
355: if (!$id) {
356: $this->_handler->halt('Metadata query failed.');
357:
358: return false;
359: }
360:
361: // made this IF due to performance (one if is faster than $count if's)
362: $count = @mysql_num_fields($id);
363: for ($i = 0; $i < $count; $i++) {
364: $res[$i]['table'] = @mysql_field_table($id, $i);
365: $res[$i]['name'] = @mysql_field_name($id, $i);
366: $res[$i]['type'] = @mysql_field_type($id, $i);
367: $res[$i]['len'] = @mysql_field_len($id, $i);
368: $res[$i]['flags'] = @mysql_field_flags($id, $i);
369: if ($full) {
370: $res['meta'][$res[$i]['name']] = $i;
371: }
372: }
373: if ($full) {
374: $res['num_fields'] = $count;
375: }
376:
377: $this->free();
378:
379: return $res;
380: }
381:
382: /**
383: * Fetches all table names.
384: *
385: * @see cDbDriverAbstract::getTableNames()
386: * @return array
387: */
388: public function getTableNames() {
389: $return = array();
390:
391: if ($this->query('SHOW TABLES')) {
392: while ($this->nextRecord()) {
393: $record = $this->getRecord();
394: $return[] = array(
395: 'table_name' => $record[0], 'tablespace_name' => $this->_dbCfg['connection']['database'], 'database' => $this->_dbCfg['connection']['database'],
396: );
397: }
398:
399: $this->free();
400: }
401:
402: return $return;
403: }
404:
405: /**
406: * Fetches server information.
407: *
408: * @see cDbDriverAbstract::getServerInfo()
409: * @return array
410: */
411: public function getServerInfo() {
412: $linkId = $this->_handler->getLinkId();
413:
414: if (is_resource($linkId)) {
415: $arr = array();
416: $arr['description'] = mysql_get_server_info($linkId);
417:
418: return $arr;
419: }
420:
421: return NULL;
422: }
423:
424: /**
425: * Returns error code of last occured error by using databases interface.
426: *
427: * @see cDbDriverAbstract::getErrorNumber()
428: * @return int
429: */
430: public function getErrorNumber() {
431: $linkId = $this->_handler->getLinkId();
432:
433: if (is_resource($linkId)) {
434: return mysql_errno($linkId);
435: } else {
436: return mysql_errno();
437: }
438: }
439:
440: /**
441: * Returns error message of last occured error by using databases interface.
442: *
443: * @see cDbDriverAbstract::getErrorMessage()
444: * @return string
445: */
446: public function getErrorMessage() {
447: $linkId = $this->_handler->getLinkId();
448:
449: if (is_resource($linkId)) {
450: return mysql_error($linkId);
451: } else {
452: return mysql_error();
453: }
454: }
455:
456: /**
457: * Closes the connection and frees the query id.
458: *
459: * @see cDbDriverAbstract::disconnect()
460: */
461: public function disconnect() {
462: mysql_close($this->_handler->getLinkId());
463: }
464:
465: }
466: