1: <?php
2: /* SVN FILE: $Id$ */
3: /**
4: * DataSource base class
5: *
6: * Long description for file
7: *
8: * PHP versions 4 and 5
9: *
10: * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
11: * Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
12: *
13: * Licensed under The MIT License
14: * Redistributions of files must retain the above copyright notice.
15: *
16: * @copyright Copyright 2005-2012, Cake Software Foundation, Inc. (http://cakefoundation.org)
17: * @link http://cakephp.org CakePHP(tm) Project
18: * @package cake
19: * @subpackage cake.cake.libs.model.datasources
20: * @since CakePHP(tm) v 0.10.5.1790
21: * @version $Revision$
22: * @modifiedby $LastChangedBy$
23: * @lastmodified $Date$
24: * @license http://www.opensource.org/licenses/mit-license.php The MIT License
25: */
26: /**
27: * DataSource base class
28: *
29: * Long description for file
30: *
31: * @package cake
32: * @subpackage cake.cake.libs.model.datasources
33: */
34: class DataSource extends Object {
35: /**
36: * Are we connected to the DataSource?
37: *
38: * @var boolean
39: * @access public
40: */
41: var $connected = false;
42: /**
43: * Print full query debug info?
44: *
45: * @var boolean
46: * @access public
47: */
48: var $fullDebug = false;
49: /**
50: * Error description of last query
51: *
52: * @var unknown_type
53: * @access public
54: */
55: var $error = null;
56: /**
57: * String to hold how many rows were affected by the last SQL operation.
58: *
59: * @var string
60: * @access public
61: */
62: var $affected = null;
63: /**
64: * Number of rows in current resultset
65: *
66: * @var int
67: * @access public
68: */
69: var $numRows = null;
70: /**
71: * Time the last query took
72: *
73: * @var int
74: * @access public
75: */
76: var $took = null;
77: /**
78: * The starting character that this DataSource uses for quoted identifiers.
79: *
80: * @var string
81: */
82: var $startQuote = null;
83: /**
84: * The ending character that this DataSource uses for quoted identifiers.
85: *
86: * @var string
87: */
88: var $endQuote = null;
89: /**
90: * Enter description here...
91: *
92: * @var array
93: * @access protected
94: */
95: var $_result = null;
96: /**
97: * Queries count.
98: *
99: * @var int
100: * @access protected
101: */
102: var $_queriesCnt = 0;
103: /**
104: * Total duration of all queries.
105: *
106: * @var unknown_type
107: * @access protected
108: */
109: var $_queriesTime = null;
110: /**
111: * Log of queries executed by this DataSource
112: *
113: * @var unknown_type
114: * @access protected
115: */
116: var $_queriesLog = array();
117: /**
118: * Maximum number of items in query log, to prevent query log taking over
119: * too much memory on large amounts of queries -- I we've had problems at
120: * >6000 queries on one system.
121: *
122: * @var int Maximum number of queries in the queries log.
123: * @access protected
124: */
125: var $_queriesLogMax = 200;
126: /**
127: * Caches serialzed results of executed queries
128: *
129: * @var array Maximum number of queries in the queries log.
130: * @access protected
131: */
132: var $_queryCache = array();
133: /**
134: * The default configuration of a specific DataSource
135: *
136: * @var array
137: * @access protected
138: */
139: var $_baseConfig = array();
140: /**
141: * Holds references to descriptions loaded by the DataSource
142: *
143: * @var array
144: * @access private
145: */
146: var $__descriptions = array();
147: /**
148: * Holds a list of sources (tables) contained in the DataSource
149: *
150: * @var array
151: * @access protected
152: */
153: var $_sources = null;
154: /**
155: * A reference to the physical connection of this DataSource
156: *
157: * @var array
158: * @access public
159: */
160: var $connection = null;
161: /**
162: * The DataSource configuration
163: *
164: * @var array
165: * @access public
166: */
167: var $config = array();
168: /**
169: * The DataSource configuration key name
170: *
171: * @var string
172: * @access public
173: */
174: var $configKeyName = null;
175: /**
176: * Whether or not this DataSource is in the middle of a transaction
177: *
178: * @var boolean
179: * @access protected
180: */
181: var $_transactionStarted = false;
182: /**
183: * Whether or not source data like available tables and schema descriptions
184: * should be cached
185: *
186: * @var boolean
187: */
188: var $cacheSources = true;
189: /**
190: * Constructor.
191: */
192: function __construct($config = array()) {
193: parent::__construct();
194: $this->setConfig($config);
195: }
196: /**
197: * Caches/returns cached results for child instances
198: *
199: * @return array
200: */
201: function listSources($data = null) {
202: if ($this->cacheSources === false) {
203: return null;
204: }
205:
206: if ($this->_sources !== null) {
207: return $this->_sources;
208: }
209:
210: $key = ConnectionManager::getSourceName($this) . '_' . $this->config['database'] . '_list';
211: $key = preg_replace('/[^A-Za-z0-9_\-.+]/', '_', $key);
212: $sources = Cache::read($key, '_cake_model_');
213:
214: if (empty($sources)) {
215: $sources = $data;
216: Cache::write($key, $data, '_cake_model_');
217: }
218:
219: $this->_sources = $sources;
220: return $sources;
221: }
222: /**
223: * Convenience method for DboSource::listSources(). Returns source names in lowercase.
224: *
225: * @return array
226: */
227: function sources($reset = false) {
228: if ($reset === true) {
229: $this->_sources = null;
230: }
231: return array_map('strtolower', $this->listSources());
232: }
233: /**
234: * Returns a Model description (metadata) or null if none found.
235: *
236: * @param Model $model
237: * @return mixed
238: */
239: function describe($model) {
240: if ($this->cacheSources === false) {
241: return null;
242: }
243: $table = $this->fullTableName($model, false);
244: if (isset($this->__descriptions[$table])) {
245: return $this->__descriptions[$table];
246: }
247: $cache = $this->__cacheDescription($table);
248:
249: if ($cache !== null) {
250: $this->__descriptions[$table] =& $cache;
251: return $cache;
252: }
253: return null;
254: }
255: /**
256: * Begin a transaction
257: *
258: * @return boolean Returns true if a transaction is not in progress
259: */
260: function begin(&$model) {
261: return !$this->_transactionStarted;
262: }
263: /**
264: * Commit a transaction
265: *
266: * @return boolean Returns true if a transaction is in progress
267: */
268: function commit(&$model) {
269: return $this->_transactionStarted;
270: }
271: /**
272: * Rollback a transaction
273: *
274: * @return boolean Returns true if a transaction is in progress
275: */
276: function rollback(&$model) {
277: return $this->_transactionStarted;
278: }
279: /**
280: * Converts column types to basic types
281: *
282: * @param string $real Real column type (i.e. "varchar(255)")
283: * @return string Abstract column type (i.e. "string")
284: */
285: function column($real) {
286: return false;
287: }
288: /**
289: * Used to create new records. The "C" CRUD.
290: *
291: * To-be-overridden in subclasses.
292: *
293: * @param Model $model The Model to be created.
294: * @param array $fields An Array of fields to be saved.
295: * @param array $values An Array of values to save.
296: * @return boolean success
297: */
298: function create(&$model, $fields = null, $values = null) {
299: return false;
300: }
301: /**
302: * Used to read records from the Datasource. The "R" in CRUD
303: *
304: * To-be-overridden in subclasses.
305: *
306: * @param Model $model The model being read.
307: * @param array $queryData An array of query data used to find the data you want
308: * @return mixed
309: */
310: function read(&$model, $queryData = array()) {
311: return false;
312: }
313: /**
314: * Update a record(s) in the datasource.
315: *
316: * To-be-overridden in subclasses.
317: *
318: * @param Model $model Instance of the model class being updated
319: * @param array $fields Array of fields to be updated
320: * @param array $values Array of values to be update $fields to.
321: * @return boolean Success
322: */
323: function update(&$model, $fields = null, $values = null) {
324: return false;
325: }
326: /**
327: * Delete a record(s) in the datasource.
328: *
329: * To-be-overridden in subclasses.
330: *
331: * @param Model $model The model class having record(s) deleted
332: * @param mixed $id Primary key of the model
333: */
334: function delete(&$model, $id = null) {
335: if ($id == null) {
336: $id = $model->id;
337: }
338: }
339: /**
340: * Returns the ID generated from the previous INSERT operation.
341: *
342: * @param unknown_type $source
343: * @return in
344: */
345: function lastInsertId($source = null) {
346: return false;
347: }
348: /**
349: * Returns the ID generated from the previous INSERT operation.
350: *
351: * @param unknown_type $source
352: * @return in
353: */
354: function lastNumRows($source = null) {
355: return false;
356: }
357: /**
358: * Returns the ID generated from the previous INSERT operation.
359: *
360: * @param unknown_type $source
361: * @return in
362: */
363: function lastAffected($source = null) {
364: return false;
365: }
366: /**
367: * Check whether the conditions for the Datasource being available
368: * are satisfied. Often used from connect() to check for support
369: * before establishing a connection.
370: *
371: * @return boolean Whether or not the Datasources conditions for use are met.
372: **/
373: function enabled() {
374: return true;
375: }
376: /**
377: * Returns true if the DataSource supports the given interface (method)
378: *
379: * @param string $interface The name of the interface (method)
380: * @return boolean True on success
381: */
382: function isInterfaceSupported($interface) {
383: $methods = get_class_methods(get_class($this));
384: $methods = strtolower(implode('|', $methods));
385: $methods = explode('|', $methods);
386: $return = in_array(strtolower($interface), $methods);
387: return $return;
388: }
389: /**
390: * Sets the configuration for the DataSource
391: *
392: * @param array $config The configuration array
393: * @return void
394: */
395: function setConfig($config = array()) {
396: $this->config = array_merge($this->_baseConfig, $this->config, $config);
397: }
398: /**
399: * Cache the DataSource description
400: *
401: * @param string $object The name of the object (model) to cache
402: * @param mixed $data The description of the model, usually a string or array
403: */
404: function __cacheDescription($object, $data = null) {
405: if ($this->cacheSources === false) {
406: return null;
407: }
408:
409: if ($data !== null) {
410: $this->__descriptions[$object] =& $data;
411: }
412:
413: $key = ConnectionManager::getSourceName($this) . '_' . $object;
414: $cache = Cache::read($key, '_cake_model_');
415:
416: if (empty($cache)) {
417: $cache = $data;
418: Cache::write($key, $cache, '_cake_model_');
419: }
420:
421: return $cache;
422: }
423: /**
424: * Enter description here...
425: *
426: * @param unknown_type $query
427: * @param unknown_type $data
428: * @param unknown_type $association
429: * @param unknown_type $assocData
430: * @param Model $model
431: * @param Model $linkModel
432: * @param array $stack
433: * @return unknown
434: */
435: function insertQueryData($query, $data, $association, $assocData, &$model, &$linkModel, $stack) {
436: $keys = array('{$__cakeID__$}', '{$__cakeForeignKey__$}');
437:
438: foreach ($keys as $key) {
439: $val = null;
440: $type = null;
441:
442: if (strpos($query, $key) !== false) {
443: switch ($key) {
444: case '{$__cakeID__$}':
445: if (isset($data[$model->alias]) || isset($data[$association])) {
446: if (isset($data[$model->alias][$model->primaryKey])) {
447: $val = $data[$model->alias][$model->primaryKey];
448: } elseif (isset($data[$association][$model->primaryKey])) {
449: $val = $data[$association][$model->primaryKey];
450: }
451: } else {
452: $found = false;
453: foreach (array_reverse($stack) as $assoc) {
454: if (isset($data[$assoc]) && isset($data[$assoc][$model->primaryKey])) {
455: $val = $data[$assoc][$model->primaryKey];
456: $found = true;
457: break;
458: }
459: }
460: if (!$found) {
461: $val = '';
462: }
463: }
464: $type = $model->getColumnType($model->primaryKey);
465: break;
466: case '{$__cakeForeignKey__$}':
467: foreach ($model->__associations as $id => $name) {
468: foreach ($model->$name as $assocName => $assoc) {
469: if ($assocName === $association) {
470: if (isset($assoc['foreignKey'])) {
471: $foreignKey = $assoc['foreignKey'];
472: $assocModel = $model->$assocName;
473: $type = $assocModel->getColumnType($assocModel->primaryKey);
474:
475: if (isset($data[$model->alias][$foreignKey])) {
476: $val = $data[$model->alias][$foreignKey];
477: } elseif (isset($data[$association][$foreignKey])) {
478: $val = $data[$association][$foreignKey];
479: } else {
480: $found = false;
481: foreach (array_reverse($stack) as $assoc) {
482: if (isset($data[$assoc]) && isset($data[$assoc][$foreignKey])) {
483: $val = $data[$assoc][$foreignKey];
484: $found = true;
485: break;
486: }
487: }
488: if (!$found) {
489: $val = '';
490: }
491: }
492: }
493: break 3;
494: }
495: }
496: }
497: break;
498: }
499: if (empty($val) && $val !== '0') {
500: return false;
501: }
502: $query = str_replace($key, $this->value($val, $type), $query);
503: }
504: }
505: return $query;
506: }
507: /**
508: * To-be-overridden in subclasses.
509: *
510: * @param unknown_type $model
511: * @param unknown_type $key
512: * @return unknown
513: */
514: function resolveKey($model, $key) {
515: return $model->alias . $key;
516: }
517: /**
518: * Closes the current datasource.
519: *
520: */
521: function __destruct() {
522: if ($this->_transactionStarted) {
523: $null = null;
524: $this->rollback($null);
525: }
526: if ($this->connected) {
527: $this->close();
528: }
529: }
530: }
531: ?>
532: