DataSource.php 13 KB

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