PageRenderTime 55ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/php/DB/QueryTool/Query.php

https://bitbucket.org/adarshj/convenient_website
PHP | 2395 lines | 957 code | 235 blank | 1203 comment | 159 complexity | 756e9ed11d2a04d0dce5dade121c5330 MD5 | raw file
Possible License(s): Apache-2.0, MPL-2.0-no-copyleft-exception, LGPL-2.1, BSD-2-Clause, GPL-2.0, LGPL-3.0
  1. <?php
  2. /* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
  3. /**
  4. * Contains the DB_QueryTool_Query class
  5. *
  6. * PHP versions 4 and 5
  7. *
  8. * LICENSE: This source file is subject to version 3.0 of the PHP license
  9. * that is available through the world-wide-web at the following URI:
  10. * http://www.php.net/license/3_0.txt. If you did not receive a copy of
  11. * the PHP License and are unable to obtain it through the web, please
  12. * send a note to license@php.net so we can mail you a copy immediately.
  13. *
  14. * @category Database
  15. * @package DB_QueryTool
  16. * @author Wolfram Kriesing <wk@visionp.de>
  17. * @author Paolo Panto <wk@visionp.de>
  18. * @author Lorenzo Alberton <l dot alberton at quipo dot it>
  19. * @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
  20. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  21. * @version CVS: $Id: Query.php,v 1.57 2005/02/27 19:13:19 quipo Exp $
  22. * @link http://pear.php.net/package/DB_QueryTool
  23. */
  24. /**
  25. * require the PEAR and DB classes
  26. */
  27. require_once 'PEAR.php';
  28. require_once 'DB.php';
  29. /**
  30. * DB_QueryTool_Query class
  31. *
  32. * This class should be extended
  33. *
  34. * @category Database
  35. * @package DB_QueryTool
  36. * @author Wolfram Kriesing <wk@visionp.de>
  37. * @author Paolo Panto <wk@visionp.de>
  38. * @author Lorenzo Alberton <l dot alberton at quipo dot it>
  39. * @copyright 2003-2005 Wolfram Kriesing, Paolo Panto, Lorenzo Alberton
  40. * @license http://www.php.net/license/3_0.txt PHP License 3.0
  41. * @link http://pear.php.net/package/DB_QueryTool
  42. */
  43. class DB_QueryTool_Query
  44. {
  45. // {{{ class vars
  46. /**
  47. * @var string the name of the primary column
  48. */
  49. var $primaryCol = 'id';
  50. /**
  51. * @var string the current table the class works on
  52. */
  53. var $table = '';
  54. /**
  55. * @var string the name of the sequence for this table
  56. */
  57. var $sequenceName = null;
  58. /**
  59. * @var object the db-object, a PEAR::DB instance
  60. */
  61. var $db = null;
  62. /**
  63. * @var string the where condition
  64. * @access private
  65. */
  66. var $_where = '';
  67. /**
  68. * @var string the order condition
  69. * @access private
  70. */
  71. var $_order = '';
  72. /**
  73. * @var string the having definition
  74. * @access private
  75. */
  76. var $_having = '';
  77. /**
  78. * @var array contains the join content
  79. * the key is the join type, for now we have 'default' and 'left'
  80. * inside each key 'table' contains the table
  81. * key 'where' contains the where clause for the join
  82. * @access private
  83. */
  84. var $_join = array();
  85. /**
  86. * @var string which column to index the result by
  87. * @access private
  88. */
  89. var $_index = null;
  90. /**
  91. * @var string the group-by clause
  92. * @access private
  93. */
  94. var $_group = '';
  95. /**
  96. * @var array the limit
  97. * @access private
  98. */
  99. var $_limit = array();
  100. /**
  101. * @var string type of result to return
  102. * @access private
  103. */
  104. var $_resultType = 'none';
  105. /**
  106. * @var array the metadata temporary saved
  107. * @access private
  108. */
  109. var $_metadata = array();
  110. /**
  111. * @var string
  112. * @access private
  113. */
  114. var $_lastQuery = null;
  115. /**
  116. * @var string the rows that shall be selected
  117. * @access private
  118. */
  119. var $_select = '*';
  120. /**
  121. * @var string the rows that shall not be selected
  122. * @access private
  123. */
  124. var $_dontSelect = '';
  125. /**
  126. * @var array this array saves different modes in which this class works
  127. * i.e. 'raw' means no quoting before saving/updating data
  128. * @access private
  129. */
  130. var $options = array(
  131. 'raw' => false,
  132. 'verbose' => true, // set this to false in a productive environment
  133. // it will produce error-logs if set to true
  134. 'useCache' => false,
  135. 'logFile' => false,
  136. );
  137. /**
  138. * this array contains information about the tables
  139. * those are
  140. * - 'name' => the real table name
  141. * - 'shortName' => the short name used, so that when moving the table i.e.
  142. * onto a provider's db and u have to rename the tables to
  143. * longer names this name will be relevant, i.e. when
  144. * autoJoining, i.e. a table name on your local machine is:
  145. * 'user' but online it has to be 'applName_user' then the
  146. * shortName will be used to determine if a column refers to
  147. * another table, if the colName is 'user_id', it knows the
  148. * shortName 'user' refers to the table 'applName_user'
  149. */
  150. var $tableSpec = array();
  151. /**
  152. * this is the regular expression that shall be used to find a table's shortName
  153. * in a column name, the string found by using this regular expression will be removed
  154. * from the column name and it will be checked if it is a table name
  155. * i.e. the default '/_id$/' would find the table name 'user' from the column name 'user_id'
  156. */
  157. var $_tableNameToShortNamePreg = '/^.*_/';
  158. /**
  159. * @var array this array caches queries that have already been built once
  160. * to reduce the execution time
  161. */
  162. var $_queryCache = array();
  163. /**
  164. * The object that contains the log-instance
  165. */
  166. var $_logObject = null;
  167. /**
  168. * Some internal data the logging needs
  169. */
  170. var $_logData = array();
  171. // }}}
  172. // {{{ __construct()
  173. /**
  174. * this is the constructor, as it will be implemented in ZE2 (php5)
  175. *
  176. * @version 2002/04/02
  177. * @access public
  178. * @author Wolfram Kriesing <wk@visionp.de>
  179. * @param object db-object
  180. */
  181. /*
  182. function __construct($dsn=false, $options=array())
  183. {
  184. if (!isset($options['autoConnect'])) {
  185. $autoConnect = true;
  186. } else {
  187. $autoConnect = $options['autoConnect'];
  188. }
  189. if (isset($options['errorCallback'])) {
  190. $this->setErrorCallback($options['errorCallback']);
  191. }
  192. if (isset($options['errorSetCallback'])) {
  193. $this->setErrorSetCallback($options['errorSetCallback']);
  194. }
  195. if (isset($options['errorLogCallback'])) {
  196. $this->setErrorLogCallback($options['errorLogCallback']);
  197. }
  198. if ($autoConnect && $dsn) {
  199. $this->connect($dsn, $options);
  200. }
  201. //we would need to parse the dsn first ... i dont feel like now :-)
  202. // oracle has all column names in upper case
  203. //FIXXXME make the class work only with upper case when we work with oracle
  204. //if ($this->db->phptype=='oci8' && !$this->primaryCol) {
  205. // $this->primaryCol = 'ID';
  206. //}
  207. if ($this->sequenceName == null) {
  208. $this->sequenceName = $this->table;
  209. }
  210. }
  211. */
  212. // }}}
  213. // {{{ DB_QueryTool_Query()
  214. /**
  215. * @version 2002/04/02
  216. * @access public
  217. * @author Wolfram Kriesing <wk@visionp.de>
  218. * @param mixed $dsn DSN string, DSN array or DB object
  219. * @param array $options
  220. */
  221. function DB_QueryTool_Query($dsn=false, $options=array())
  222. {
  223. //$this->__construct($dsn, $options);
  224. if (!isset($options['autoConnect'])) {
  225. $autoConnect = true;
  226. } else {
  227. $autoConnect = $options['autoConnect'];
  228. unset($options['autoConnect']);
  229. }
  230. if (isset($options['errorCallback'])) {
  231. $this->setErrorCallback($options['errorCallback']);
  232. unset($options['errorCallback']);
  233. }
  234. if (isset($options['errorSetCallback'])) {
  235. $this->setErrorSetCallback($options['errorSetCallback']);
  236. unset($options['errorSetCallback']);
  237. }
  238. if (isset($options['errorLogCallback'])) {
  239. $this->setErrorLogCallback($options['errorLogCallback']);
  240. unset($options['errorLogCallback']);
  241. }
  242. if ($autoConnect && $dsn) {
  243. $this->connect($dsn, $options);
  244. }
  245. if (is_null($this->sequenceName)) {
  246. $this->sequenceName = $this->table;
  247. }
  248. }
  249. // }}}
  250. // {{{ connect()
  251. /**
  252. * use this method if you want to connect manually
  253. * @param mixed $dsn DSN string, DSN array or MDB object
  254. * @param array $options
  255. */
  256. function connect($dsn, $options=array())
  257. {
  258. if (is_object($dsn)) {
  259. $res = $this->db =& $dsn;
  260. } else {
  261. $res = $this->db = DB::connect($dsn, $options);
  262. }
  263. if (DB::isError($res)) {
  264. // FIXXME what shall we do here?
  265. $this->_errorLog($res->getUserInfo());
  266. } else {
  267. $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
  268. }
  269. }
  270. // }}}
  271. // {{{ getDbInstance()
  272. /**
  273. * @return reference to current DB instance
  274. */
  275. function &getDbInstance()
  276. {
  277. return $this->db;
  278. }
  279. // }}}
  280. // {{{ setDbInstance()
  281. /**
  282. * Setup using an existing connection.
  283. * this also sets the DB_FETCHMODE_ASSOC since this class
  284. * needs this to be set!
  285. *
  286. * @param object a reference to an existing DB-object
  287. * @return void
  288. */
  289. function setDbInstance(&$dbh)
  290. {
  291. $this->db =& $dbh;
  292. $this->db->setFetchMode(DB_FETCHMODE_ASSOC);
  293. }
  294. // }}}
  295. // {{{ get()
  296. /**
  297. * get the data of a single entry
  298. * if the second parameter is only one column the result will be returned
  299. * directly not as an array!
  300. *
  301. * @version 2002/03/05
  302. * @access public
  303. * @author Wolfram Kriesing <wk@visionp.de>
  304. * @param integer the id of the element to retrieve
  305. * @param string if this is given only one row shall be returned, directly, not an array
  306. * @return mixed (1) an array of the retrieved data
  307. * (2) if the second parameter is given and its only one column,
  308. * only this column's data will be returned
  309. * (3) false in case of failure
  310. */
  311. function get($id, $column='')
  312. {
  313. $table = $this->table;
  314. $getMethod = 'getRow';
  315. if ($column && !strpos($column, ',')) { // if only one column shall be selected
  316. $getMethod = 'getOne';
  317. }
  318. // we dont use 'setSelect' here, since this changes the setup of the class, we
  319. // build the query directly
  320. // if $column is '' then _buildSelect selects '*' anyway, so that's the same behaviour as before
  321. $query['select'] = $this->_buildSelect($column);
  322. $query['where'] = $this->_buildWhere($this->table.'.'.$this->primaryCol.'='.$id);
  323. $queryString = $this->_buildSelectQuery($query);
  324. return $this->returnResult($this->execute($queryString,$getMethod));
  325. }
  326. // }}}
  327. // {{{ getMultiple()
  328. /**
  329. * gets the data of the given ids
  330. *
  331. * @version 2002/04/23
  332. * @access public
  333. * @author Wolfram Kriesing <wk@visionp.de>
  334. * @param array this is an array of ids to retreive
  335. * @param string the column to search in for
  336. * @return mixed an array of the retreived data, or false in case of failure
  337. * when failing an error is set in $this->_error
  338. */
  339. function getMultiple($ids, $column='')
  340. {
  341. $col = $this->primaryCol;
  342. if ($column) {
  343. $col = $column;
  344. }
  345. // FIXXME if $ids has no table.col syntax and we are using joins, the table better be put in front!!!
  346. $ids = $this->_quoteArray($ids);
  347. $query['where'] = $this->_buildWhere($col.' IN ('.implode(',', $ids).')');
  348. $queryString = $this->_buildSelectQuery($query);
  349. return $this->returnResult($this->execute($queryString));
  350. }
  351. // }}}
  352. // {{{ getAll()
  353. /**
  354. * get all entries from the DB
  355. * for sorting use setOrder!!!, the last 2 parameters are deprecated
  356. *
  357. * @version 2002/03/05
  358. * @access public
  359. * @author Wolfram Kriesing <wk@visionp.de>
  360. * @param int to start from
  361. * @param int the number of rows to show
  362. * @param string the DB-method to use, i dont know if we should leave this param here ...
  363. * @return mixed an array of the retreived data, or false in case of failure
  364. * when failing an error is set in $this->_error
  365. */
  366. function getAll($from=0,$count=0,$method='getAll')
  367. {
  368. $query = array();
  369. if ($count) {
  370. $query = array('limit' => array($from, $count));
  371. }
  372. return $this->returnResult($this->execute($this->_buildSelectQuery($query), $method));
  373. }
  374. // }}}
  375. // {{{ getCol()
  376. /**
  377. * this method only returns one column, so the result will be a one dimensional array
  378. * this does also mean that using setSelect() should be set to *one* column, the one you want to
  379. * have returned a most common use case for this could be:
  380. * $table->setSelect('id');
  381. * $ids = $table->getCol();
  382. * OR
  383. * $ids = $table->getCol('id');
  384. * so ids will be an array with all the id's
  385. *
  386. * @version 2003/02/25
  387. * @access public
  388. * @author Wolfram Kriesing <wk@visionp.de>
  389. * @param string the column that shall be retreived
  390. * @param int to start from
  391. * @param int the number of rows to show
  392. * @return mixed an array of the retreived data, or false in case of failure
  393. * when failing an error is set in $this->_error
  394. */
  395. function getCol($column=null, $from=0, $count=0)
  396. {
  397. $query = array();
  398. if (!is_null($column)) {
  399. // by using _buildSelect() i can be sure that the table name will not be ambigious
  400. // i.e. in a join, where all the joined tables have a col 'id'
  401. // _buildSelect() will put the proper table name in front in case there is none
  402. $query['select'] = $this->_buildSelect($column);
  403. }
  404. if ($count) {
  405. $query['limit'] = array($from,$count);
  406. }
  407. return $this->returnResult($this->execute($this->_buildSelectQuery($query), 'getCol'));
  408. }
  409. // }}}
  410. // {{{ getCount()
  411. /**
  412. * get the number of entries
  413. *
  414. * @version 2002/04/02
  415. * @access public
  416. * @author Wolfram Kriesing <wk@visionp.de>
  417. * @param
  418. * @return mixed an array of the retreived data, or false in case of failure
  419. * when failing an error is set in $this->_error
  420. */
  421. function getCount()
  422. {
  423. /* the following query works on mysql
  424. SELECT count(DISTINCT image.id) FROM image2tree
  425. RIGHT JOIN image ON image.id = image2tree.image_id
  426. the reason why this is needed - i just wanted to get the number of rows that do exist if the result is grouped by image.id
  427. the following query is what i tried first, but that returns the number of rows that have been grouped together
  428. for each image.id
  429. SELECT count(*) FROM image2tree
  430. RIGHT JOIN image ON image.id = image2tree.image_id GROUP BY image.id
  431. so that's why we do the following, i am not sure if that is standard SQL and absolutley correct!!!
  432. */
  433. //FIXXME see comment above if this is absolutely correct!!!
  434. if ($group = $this->_buildGroup()) {
  435. $query['select'] = 'COUNT(DISTINCT '.$group.')';
  436. $query['group'] = '';
  437. } else {
  438. $query['select'] = 'COUNT(*)';
  439. }
  440. $query['order'] = ''; // order is not of importance and might freak up the special group-handling up there, since the order-col is not be known
  441. /*# FIXXME use the following line, but watch out, then it has to be used in every method, or this
  442. # value will be used always, simply try calling getCount and getAll afterwards, getAll will return the count :-)
  443. # if getAll doesn't use setSelect!!!
  444. */
  445. //$this->setSelect('count(*)');
  446. $queryString = $this->_buildSelectQuery($query, true);
  447. return ($res = $this->execute($queryString, 'getOne')) ? $res : 0;
  448. }
  449. // }}}
  450. // {{{ getDefaultValues()
  451. /**
  452. * return an empty element where all the array elements do already exist
  453. * corresponding to the columns in the DB
  454. *
  455. * @version 2002/04/05
  456. * @access public
  457. * @author Wolfram Kriesing <wk@visionp.de>
  458. * @return array an empty, or pre-initialized element
  459. */
  460. function getDefaultValues()
  461. {
  462. $ret = array();
  463. // here we read all the columns from the DB and initialize them
  464. // with '' to prevent PHP-warnings in case we use error_reporting=E_ALL
  465. foreach ($this->metadata() as $aCol=>$x) {
  466. $ret[$aCol] = '';
  467. }
  468. return $ret;
  469. }
  470. // }}}
  471. // {{{ getEmptyElement()
  472. /**
  473. * this is just for BC
  474. * @deprecated
  475. */
  476. function getEmptyElement()
  477. {
  478. $this->getDefaultValues();
  479. }
  480. // }}}
  481. // {{{ getQueryString()
  482. /**
  483. * Render the current query and return it as a string.
  484. *
  485. * @return string the current query
  486. */
  487. function getQueryString()
  488. {
  489. $ret = $this->_buildSelectQuery();
  490. if (is_string($ret)) {
  491. $ret = trim($ret);
  492. }
  493. return $ret;
  494. }
  495. // }}}
  496. // {{{ save()
  497. /**
  498. * save data, calls either update or add
  499. * if the primaryCol is given in the data this method knows that the
  500. * data passed to it are meant to be updated (call 'update'), otherwise it will
  501. * call the method 'add'.
  502. * If you dont like this behaviour simply stick with the methods 'add'
  503. * and 'update' and ignore this one here.
  504. * This method is very useful when you have validation checks that have to
  505. * be done for both adding and updating, then you can simply overwrite this
  506. * method and do the checks in here, and both cases will be validated first.
  507. *
  508. * @version 2002/03/11
  509. * @access public
  510. * @author Wolfram Kriesing <wk@visionp.de>
  511. * @param array contains the new data that shall be saved in the DB
  512. * @return mixed the data returned by either add or update-method
  513. */
  514. function save($data)
  515. {
  516. if (!empty($data[$this->primaryCol])) {
  517. return $this->update($data);
  518. }
  519. return $this->add($data);
  520. }
  521. // }}}
  522. // {{{ update()
  523. /**
  524. * update the member data of a data set
  525. *
  526. * @version 2002/03/06
  527. * @access public
  528. * @author Wolfram Kriesing <wk@visionp.de>
  529. * @param array contains the new data that shall be saved in the DB
  530. * the id has to be given in the field with the key 'ID'
  531. * @return mixed true on success, or false otherwise
  532. */
  533. function update($newData)
  534. {
  535. $query = array();
  536. // do only set the 'where' part in $query, if a primary column is given
  537. // if not the default 'where' clause is used
  538. if (isset($newData[$this->primaryCol])) {
  539. $query['where'] = $this->primaryCol.'='.$newData[$this->primaryCol];
  540. }
  541. $newData = $this->_checkColumns($newData, 'update');
  542. $values = array();
  543. $raw = $this->getOption('raw');
  544. foreach ($newData as $key => $aData) { // quote the data
  545. //$values[] = "{$this->table}.$key=". ($raw ? $aData : $this->db->quote($aData));
  546. $values[] = "$key=". ($raw ? $aData : $this->db->quote($aData));
  547. }
  548. $query['set'] = implode(',', $values);
  549. //FIXXXME _buildUpdateQuery() seems to take joins into account, whcih is bullshit here
  550. $updateString = $this->_buildUpdateQuery($query);
  551. #print '$updateString = '.$updateString;
  552. return $this->execute($updateString, 'query') ? true : false;
  553. }
  554. // }}}
  555. // {{{ add()
  556. /**
  557. * add a new member in the DB
  558. *
  559. * @version 2002/04/02
  560. * @access public
  561. * @author Wolfram Kriesing <wk@visionp.de>
  562. * @param array contains the new data that shall be saved in the DB
  563. * @return mixed the inserted id on success, or false otherwise
  564. */
  565. function add($newData)
  566. {
  567. // if no primary col is given, get next sequence value
  568. if (empty($newData[$this->primaryCol])) {
  569. if ($this->primaryCol) { // do only use the sequence if a primary column is given
  570. // otherwise the data are written as given
  571. $id = $this->db->nextId($this->sequenceName);
  572. $newData[$this->primaryCol] = (int)$id;
  573. } else {
  574. // if no primary col is given return true on success
  575. $id = true;
  576. }
  577. } else {
  578. $id = $newData[$this->primaryCol];
  579. }
  580. //unset($newData[$this->primaryCol]);
  581. $newData = $this->_checkColumns($newData, 'add');
  582. $newData = $this->_quoteArray($newData);
  583. $query = sprintf( 'INSERT INTO %s (%s) VALUES (%s)',
  584. $this->table,
  585. implode(', ', array_keys($newData)),
  586. implode(', ', $newData)
  587. );
  588. return $this->execute($query, 'query') ? $id : false;
  589. }
  590. // }}}
  591. // {{{ addMultiple()
  592. /**
  593. * adds multiple new members in the DB
  594. *
  595. * @version 2002/07/17
  596. * @access public
  597. * @author Wolfram Kriesing <wk@visionp.de>
  598. * @param array contains an array of new data that shall be saved in the DB
  599. * the key-value pairs have to be the same for all the data!!!
  600. * @return mixed the inserted ids on success, or false otherwise
  601. */
  602. function addMultiple($data)
  603. {
  604. if (!sizeof($data)) {
  605. return false;
  606. }
  607. // the inserted ids which will be returned or if no primaryCol is given
  608. // we return true by default
  609. $retIds = $this->primaryCol ? array() : true;
  610. $allData = array(); // each row that will be inserted
  611. foreach ($data as $key => $aData) {
  612. $aData = $this->_checkColumns($aData, 'add');
  613. $aData = $this->_quoteArray($aData);
  614. if (empty($aData[$this->primaryCol])) {
  615. if ($this->primaryCol) { // do only use the sequence if a primary column is given
  616. // otherwise the data are written as given
  617. $retIds[] = $id = (int)$this->db->nextId($this->sequenceName);
  618. $aData[$this->primaryCol] = $id;
  619. }
  620. } else {
  621. $retIds[] = $aData[$this->primaryCol];
  622. }
  623. $allData[] = '('.implode(', ', $aData).')';
  624. }
  625. $query = sprintf( 'INSERT INTO %s (%s) VALUES %s',
  626. $this->table,
  627. implode(', ', array_keys($aData)), // use the keys of the last element built
  628. implode(', ', $allData)
  629. );
  630. return $this->execute($query, 'query') ? $retIds : false;
  631. }
  632. // }}}
  633. // {{{ remove()
  634. /**
  635. * removes a member from the DB
  636. *
  637. * @version 2002/04/08
  638. * @access public
  639. * @author Wolfram Kriesing <wk@visionp.de>
  640. * @param mixed integer/string - the value of the column that shall be removed
  641. * array - multiple columns that shall be matched, the second parameter will be ignored
  642. * @param string the column to match the data against, only if $data is not an array
  643. * @return boolean
  644. */
  645. function remove($data, $whereCol='')
  646. {
  647. $raw = $this->getOption('raw');
  648. if (is_array($data)) {
  649. //FIXXME check $data if it only contains columns that really exist in the table
  650. $wheres = array();
  651. foreach ($data as $key => $val) {
  652. $wheres[] = $key.'='. ($raw ? $val : $this->db->quote($val));
  653. }
  654. $whereClause = implode(' AND ',$wheres);
  655. } else {
  656. if (empty($whereCol)) {
  657. $whereCol = $this->primaryCol;
  658. }
  659. $whereClause = $whereCol.'='. ($raw ? $data : $this->db->quote($data));
  660. }
  661. $query = sprintf( 'DELETE FROM %s WHERE %s',
  662. $this->table,
  663. $whereClause
  664. );
  665. return $this->execute($query, 'query') ? true : false;
  666. // i think this method should return the ID's that it removed, this way we could simply use the result
  667. // for further actions that depend on those id ... or? make stuff easier, see ignaz::imail::remove
  668. }
  669. // }}}
  670. // {{{ removeAll()
  671. /**
  672. * empty a table
  673. *
  674. * @version 2002/06/17
  675. * @access public
  676. * @author Wolfram Kriesing <wk@visionp.de>
  677. * @return
  678. */
  679. function removeAll()
  680. {
  681. $query = 'DELETE FROM '.$this->table;
  682. return $this->execute($query, 'query') ? true : false;
  683. }
  684. // }}}
  685. // {{{ removeMultiple()
  686. /**
  687. * remove the datasets with the given ids
  688. *
  689. * @version 2002/04/24
  690. * @access public
  691. * @author Wolfram Kriesing <wk@visionp.de>
  692. * @param array the ids to remove
  693. * @return
  694. */
  695. function removeMultiple($ids, $colName='')
  696. {
  697. if (empty($colName)) {
  698. $colName = $this->primaryCol;
  699. }
  700. $ids = $this->_quoteArray($ids);
  701. $query = sprintf( 'DELETE FROM %s WHERE %s IN (%s)',
  702. $this->table,
  703. $colName,
  704. implode(',', $ids)
  705. );
  706. return $this->execute($query, 'query') ? true : false;
  707. }
  708. // }}}
  709. // {{{ removePrimary()
  710. /**
  711. * removes a member from the DB and calls the remove methods of the given objects
  712. * so all rows in another table that refer to this table are erased too
  713. *
  714. * @version 2002/04/08
  715. * @access public
  716. * @author Wolfram Kriesing <wk@visionp.de>
  717. * @param integer the value of the primary key
  718. * @param string the column name of the tables with the foreign keys
  719. * @param object just for convinience, so nobody forgets to call this method
  720. * with at least one object as a parameter
  721. * @return boolean
  722. */
  723. function removePrimary($id, $colName, $atLeastOneObject)
  724. {
  725. $argCounter = 2; // we have 2 parameters that need to be given at least
  726. // func_get_arg returns false and a warning if there are no more parameters, so
  727. // we suppress the warning and check for false
  728. while ($object = @func_get_arg($argCounter++)) {
  729. //FIXXXME let $object also simply be a table name
  730. if (!$object->remove($id, $colName)) {
  731. //FIXXXME do this better
  732. $this->_errorSet("Error removing '$colName=$id' from table {$object->table}.");
  733. return false;
  734. }
  735. }
  736. return ($this->remove($id) ? true : false);
  737. }
  738. // }}}
  739. // {{{ setLimit()
  740. /**
  741. * @param integer $from
  742. * @param integer $count
  743. */
  744. function setLimit($from=0, $count=0)
  745. {
  746. if ($from==0 && $count==0) {
  747. $this->_limit = array();
  748. } else {
  749. $this->_limit = array($from, $count);
  750. }
  751. }
  752. // }}}
  753. // {{{ getLimit()
  754. /**
  755. * @return array
  756. */
  757. function getLimit()
  758. {
  759. return $this->_limit;
  760. }
  761. // }}}
  762. // {{{ setWhere()
  763. /**
  764. * sets the where condition which is used for the current instance
  765. *
  766. * @version 2002/04/16
  767. * @access public
  768. * @author Wolfram Kriesing <wk@visionp.de>
  769. * @param string the where condition, this can be complete like 'X=7 AND Y=8'
  770. */
  771. function setWhere($whereCondition='')
  772. {
  773. $this->_where = $whereCondition;
  774. //FIXXME parse the where condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'"
  775. // then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
  776. }
  777. // }}}
  778. // {{{ getWhere()
  779. /**
  780. * gets the where condition which is used for the current instance
  781. *
  782. * @version 2002/04/22
  783. * @access public
  784. * @author Wolfram Kriesing <wk@visionp.de>
  785. * @return string the where condition, this can be complete like 'X=7 AND Y=8'
  786. */
  787. function getWhere()
  788. {
  789. return $this->_where;
  790. }
  791. // }}}
  792. // {{{ addWhere()
  793. /**
  794. * only adds a string to the where clause
  795. *
  796. * @version 2002/07/22
  797. * @access public
  798. * @author Wolfram Kriesing <wk@visionp.de>
  799. * @param string the where clause to add to the existing one
  800. * @param string the condition for how to concatenate the new where clause
  801. * to the existing one
  802. */
  803. function addWhere($where, $condition='AND')
  804. {
  805. if ($this->getWhere()) {
  806. $where = $this->getWhere().' '.$condition.' '.$where;
  807. }
  808. $this->setWhere($where);
  809. }
  810. // }}}
  811. // {{{ addWhereSearch()
  812. /**
  813. * add a where-like clause which works like a search for the given string
  814. * i.e. calling it like this:
  815. * $this->addWhereSearch('name', 'otto hans')
  816. * produces a where clause like this one
  817. * LOWER(name) LIKE "%otto%hans%"
  818. * so the search finds the given string
  819. *
  820. * @version 2002/08/14
  821. * @access public
  822. * @author Wolfram Kriesing <wk@visionp.de>
  823. * @param string the column to search in for
  824. * @param string the string to search for
  825. */
  826. function addWhereSearch($column, $string, $condition='AND')
  827. {
  828. // if the column doesn't contain a tablename use the current table name
  829. // in case it is a defined column to prevent ambiguous rows
  830. if (strpos($column, '.') === false) {
  831. $meta = $this->metadata();
  832. if (isset($meta[$column])) {
  833. $column = $this->table.".$column";
  834. }
  835. }
  836. $string = $this->db->quote('%'.str_replace(' ', '%', strtolower($string)).'%');
  837. $this->addWhere("LOWER($column) LIKE $string", $condition);
  838. }
  839. // }}}
  840. // {{{ setOrder()
  841. /**
  842. * sets the order condition which is used for the current instance
  843. *
  844. * @version 2002/05/16
  845. * @access public
  846. * @author Wolfram Kriesing <wk@visionp.de>
  847. * @param string the where condition, this can be complete like 'X=7 AND Y=8'
  848. * @param boolean sorting order (TRUE => ASC, FALSE => DESC)
  849. */
  850. function setOrder($orderCondition='', $desc=false)
  851. {
  852. $this->_order = $orderCondition .($desc ? ' DESC' : '');
  853. }
  854. // }}}
  855. // {{{ addOrder()
  856. /**
  857. * Add a order parameter to the query.
  858. *
  859. * @version 2003/05/28
  860. * @access public
  861. * @author Wolfram Kriesing <wk@visionp.de>
  862. * @param string the where condition, this can be complete like 'X=7 AND Y=8'
  863. * @param boolean sorting order (TRUE => ASC, FALSE => DESC)
  864. */
  865. function addOrder($orderCondition='', $desc=false)
  866. {
  867. $order = $orderCondition .($desc ? ' DESC' : '');
  868. if ($this->_order) {
  869. $this->_order = $this->_order.','.$order;
  870. } else {
  871. $this->_order = $order;
  872. }
  873. }
  874. // }}}
  875. // {{{ getOrder()
  876. /**
  877. * gets the order condition which is used for the current instance
  878. *
  879. * @version 2002/05/16
  880. * @access public
  881. * @author Wolfram Kriesing <wk@visionp.de>
  882. * @return string the order condition, this can be complete like 'ID,TIMESTAMP DESC'
  883. */
  884. function getOrder()
  885. {
  886. return $this->_order;
  887. }
  888. // }}}
  889. // {{{ setHaving()
  890. /**
  891. * sets the having definition
  892. *
  893. * @version 2003/06/05
  894. * @access public
  895. * @author Johannes Schaefer <johnschaefer@gmx.de>
  896. * @param string the having definition
  897. */
  898. function setHaving($having='')
  899. {
  900. $this->_having = $having;
  901. }
  902. // }}}
  903. // {{{ getHaving()
  904. /**
  905. * gets the having definition which is used for the current instance
  906. *
  907. * @version 2003/06/05
  908. * @access public
  909. * @author Johannes Schaefer <johnschaefer@gmx.de>
  910. * @return string the having definition
  911. */
  912. function getHaving()
  913. {
  914. return $this->_having;
  915. }
  916. // }}}
  917. // {{{ addHaving()
  918. /**
  919. * Extend the current having clause. This is very useful, when you are building
  920. * this clause from different places and don't want to overwrite the currently
  921. * set having clause, but extend it.
  922. *
  923. * @param string this is a having clause, i.e. 'column' or 'table.column' or 'MAX(column)'
  924. * @param string the connection string, which usually stays the default, which is ',' (a comma)
  925. */
  926. function addHaving($what='*', $connectString=' AND ')
  927. {
  928. if ($this->_having) {
  929. $this->_having = $this->_having.$connectString.$what;
  930. } else {
  931. $this->_having = $what;
  932. }
  933. }
  934. // }}}
  935. // {{{ setJoin()
  936. /**
  937. * sets a join-condition
  938. *
  939. * @version 2002/06/10
  940. * @access public
  941. * @author Wolfram Kriesing <wk@visionp.de>
  942. * @param mixed either a string or an array that contains
  943. * the table(s) to join on the current table
  944. * @param string the where clause for the join
  945. */
  946. function setJoin($table=null, $where=null, $joinType='default')
  947. {
  948. //FIXXME make it possible to pass a table name as a string like this too 'user u'
  949. // where u is the string that can be used to refer to this table in a where/order
  950. // or whatever condition
  951. // this way it will be possible to join tables with itself, like setJoin(array('user u','user u1'))
  952. // this wouldnt work yet, but for doing so we would need to change the _build methods too!!!
  953. // because they use getJoin('tables') and this simply returns all the tables in use
  954. // but don't take care of the mentioned syntax
  955. if (is_null($table) || is_null($where)) { // remove the join if not sufficient parameters are given
  956. $this->_join[$joinType] = array();
  957. return;
  958. }
  959. /* this causes problems if we use the order-by, since it doenst know the name to order it by ... :-)
  960. // replace the table names with the internal name used for the join
  961. // this way we can also join one table multiple times if it will be implemented one day
  962. $this->_join[$table] = preg_replace('/'.$table.'/','j1',$where);
  963. */
  964. $this->_join[$joinType][$table] = $where;
  965. }
  966. // }}}
  967. // {{{ setJoin()
  968. /**
  969. * if you do a left join on $this->table you will get all entries
  970. * from $this->table, also if there are no entries for them in the joined table
  971. * if both parameters are not given the left-join will be removed
  972. * NOTE: be sure to only use either a right or a left join
  973. *
  974. * @version 2002/07/22
  975. * @access public
  976. * @author Wolfram Kriesing <wk@visionp.de>
  977. * @param string the table(s) to be left-joined
  978. * @param string the where clause for the join
  979. */
  980. function setLeftJoin($table=null, $where=null)
  981. {
  982. $this->setJoin($table, $where, 'left');
  983. }
  984. // }}}
  985. // {{{ addLeftJoin()
  986. /**
  987. * @param string the table to be left-joined
  988. * @param string the where clause for the join
  989. * @param string the join type
  990. */
  991. function addLeftJoin($table, $where, $type='left')
  992. {
  993. // init value, to prevent E_ALL-warning
  994. if (!isset($this->_join[$type]) || !$this->_join[$type]) {
  995. $this->_join[$type] = array();
  996. }
  997. $this->_join[$type][$table] = $where;
  998. }
  999. // }}}
  1000. // {{{ setRightJoin()
  1001. /**
  1002. * see setLeftJoin for further explaination on what a left/right join is
  1003. * NOTE: be sure to only use either a right or a left join
  1004. //FIXXME check if the above sentence is necessary and if sql doesnt allow the use of both
  1005. *
  1006. * @see setLeftJoin()
  1007. * @version 2002/09/04
  1008. * @access public
  1009. * @author Wolfram Kriesing <wk@visionp.de>
  1010. * @param string the table(s) to be right-joined
  1011. * @param string the where clause for the join
  1012. */
  1013. function setRightJoin($table=null, $where=null)
  1014. {
  1015. $this->setJoin($table, $where, 'right');
  1016. }
  1017. // }}}
  1018. // {{{ getJoin()
  1019. /**
  1020. * gets the join-condition
  1021. *
  1022. * @access public
  1023. * @param string [null|''|'table'|'tables'|'right'|'left']
  1024. * @return array gets the join parameters
  1025. */
  1026. function getJoin($what=null)
  1027. {
  1028. // if the user requests all the join data or if the join is empty, return it
  1029. if (is_null($what) || empty($this->_join)) {
  1030. return $this->_join;
  1031. }
  1032. $ret = array();
  1033. switch (strtolower($what)) {
  1034. case 'table':
  1035. case 'tables':
  1036. foreach ($this->_join as $aJoin) {
  1037. if (count($aJoin)) {
  1038. $ret = array_merge($ret, array_keys($aJoin));
  1039. }
  1040. }
  1041. break;
  1042. case 'right': // return right-join data only
  1043. case 'left': // return left join data only
  1044. if (count($this->_join[$what])) {
  1045. $ret = array_merge($ret, $this->_join[$what]);
  1046. }
  1047. break;
  1048. }
  1049. return $ret;
  1050. }
  1051. // }}}
  1052. // {{{ addJoin()
  1053. /**
  1054. * adds a table and a where clause that shall be used for the join
  1055. * instead of calling
  1056. * setJoin(array(table1,table2),'<where clause1> AND <where clause2>')
  1057. * you can also call
  1058. * setJoin(table1,'<where clause1>')
  1059. * addJoin(table2,'<where clause2>')
  1060. * or where it makes more sense is to build a query which is made out of a
  1061. * left join and a standard join
  1062. * setLeftJoin(table1,'<where clause1>')
  1063. * // results in ... FROM $this->table LEFT JOIN table ON <where clause1>
  1064. * addJoin(table2,'<where clause2>')
  1065. * // results in ... FROM $this->table,table2 LEFT JOIN table ON <where clause1> WHERE <where clause2>
  1066. *
  1067. * @access public
  1068. * @param string the table to be joined
  1069. * @param string the where clause for the join
  1070. * @param string the join type
  1071. */
  1072. function addJoin($table, $where, $type='default')
  1073. {
  1074. if ($table == $this->table) {
  1075. return; //skip. Self joins are not supported.
  1076. }
  1077. // init value, to prevent E_ALL-warning
  1078. if (!isset($this->_join[$type]) || !$this->_join[$type]) {
  1079. $this->_join[$type] = array();
  1080. }
  1081. $this->_join[$type][$table] = $where;
  1082. }
  1083. // }}}
  1084. // {{{ setTable()
  1085. /**
  1086. * sets the table this class is currently working on
  1087. *
  1088. * @version 2002/07/11
  1089. * @access public
  1090. * @author Wolfram Kriesing <wk@visionp.de>
  1091. * @param string the table name
  1092. */
  1093. function setTable($table)
  1094. {
  1095. $this->table = $table;
  1096. }
  1097. // }}}
  1098. // {{{ getTable()
  1099. /**
  1100. * gets the table this class is currently working on
  1101. *
  1102. * @version 2002/07/11
  1103. * @access public
  1104. * @author Wolfram Kriesing <wk@visionp.de>
  1105. * @return string the table name
  1106. */
  1107. function getTable()
  1108. {
  1109. return $this->table;
  1110. }
  1111. // }}}
  1112. // {{{ setGroup()
  1113. /**
  1114. * sets the group-by condition
  1115. *
  1116. * @version 2002/07/22
  1117. * @access public
  1118. * @author Wolfram Kriesing <wk@visionp.de>
  1119. * @param string the group condition
  1120. */
  1121. function setGroup($group='')
  1122. {
  1123. $this->_group = $group;
  1124. //FIXXME parse the condition and replace ambigious column names, such as "name='Deutschland'" with "country.name='Deutschland'"
  1125. // then the users dont have to write that explicitly and can use the same name as in the setOrder i.e. setOrder('name,_net_name,_netPrefix_prefix');
  1126. }
  1127. // }}}
  1128. // {{{ getGroup()
  1129. /**
  1130. * gets the group condition which is used for the current instance
  1131. *
  1132. * @version 2002/07/22
  1133. * @access public
  1134. * @author Wolfram Kriesing <wk@visionp.de>
  1135. * @return string the group condition
  1136. */
  1137. function getGroup()
  1138. {
  1139. return $this->_group;
  1140. }
  1141. // }}}
  1142. // {{{ setSelect()
  1143. /**
  1144. * limit the result to return only the columns given in $what
  1145. * @param string fields that shall be selected
  1146. */
  1147. function setSelect($what='*')
  1148. {
  1149. $this->_select = $what;
  1150. }
  1151. // }}}
  1152. // {{{ addSelect()
  1153. /**
  1154. * add a string to the select part of the query
  1155. *
  1156. * add a string to the select-part of the query and connects it to an existing
  1157. * string using the $connectString, which by default is a comma.
  1158. * (SELECT xxx FROM - xxx is the select-part of a query)
  1159. *
  1160. * @version 2003/01/08
  1161. * @access public
  1162. * @author Wolfram Kriesing <wk@visionp.de>
  1163. * @param string the string that shall be added to the select-part
  1164. * @param string the string to connect the new string with the existing one
  1165. * @return void
  1166. */
  1167. function addSelect($what='*', $connectString=',')
  1168. {
  1169. // if the select string is not empty add the string, otherwise simply set it
  1170. if ($this->_select) {
  1171. $this->_select = $this->_select.$connectString.$what;
  1172. } else {
  1173. $this->_select = $what;
  1174. }
  1175. }
  1176. // }}}
  1177. // {{{ getSelect()
  1178. /**
  1179. * @return string
  1180. */
  1181. function getSelect()
  1182. {
  1183. return $this->_select;
  1184. }
  1185. // }}}
  1186. // {{{ setDontSelect()
  1187. /**
  1188. * @param string
  1189. */
  1190. function setDontSelect($what='')
  1191. {
  1192. $this->_dontSelect = $what;
  1193. }
  1194. // }}}
  1195. // {{{ getDontSelect()
  1196. /**
  1197. * @return string
  1198. */
  1199. function getDontSelect()
  1200. {
  1201. return $this->_dontSelect;
  1202. }
  1203. // }}}
  1204. // {{{ reset()
  1205. /**
  1206. * reset all the set* settings; with no parameter given, it resets them all
  1207. *
  1208. * @version 2002/09/16
  1209. * @access public
  1210. * @author Wolfram Kriesing <wk@visionp.de>
  1211. * @return void
  1212. */
  1213. function reset($what=array())
  1214. {
  1215. if (!sizeof($what)) {
  1216. $what = array(
  1217. 'select',
  1218. 'dontSelect',
  1219. 'group',
  1220. 'having',
  1221. 'limit',
  1222. 'where',
  1223. 'index',
  1224. 'order',
  1225. 'join',
  1226. 'leftJoin',
  1227. 'rightJoin'
  1228. );
  1229. }
  1230. foreach ($what as $aReset) {
  1231. $this->{'set'.ucfirst($aReset)}();
  1232. }
  1233. }
  1234. // }}}
  1235. // {{{ setOption()
  1236. /**
  1237. * set mode the class shall work in
  1238. * currently we have the modes:
  1239. * 'raw' does not quote the data before building the query
  1240. *
  1241. * @version 2002/09/17
  1242. * @access public
  1243. * @author Wolfram Kriesing <wk@visionp.de>
  1244. * @param string the mode to be set
  1245. * @param mixed the value of the mode
  1246. * @return void
  1247. */
  1248. function setOption($option, $value)
  1249. {
  1250. $this->options[strtolower($option)] = $value;
  1251. }
  1252. // }}}
  1253. // {{{ getOption()
  1254. /**
  1255. * @return string
  1256. */
  1257. function getOption($option)
  1258. {
  1259. return $this->options[strtolower($option)];
  1260. }
  1261. // }}}
  1262. // {{{ _quoteArray()
  1263. /**
  1264. * quotes all the data in this array if we are not in raw mode!
  1265. * @param array
  1266. */
  1267. function _quoteArray($data)
  1268. {
  1269. if (!$this->getOption('raw')) {
  1270. foreach ($data as $key => $val) {
  1271. $data[$key] = $this->db->quote($val);
  1272. }
  1273. }
  1274. return $data;
  1275. }
  1276. // }}}
  1277. // {{{ _checkColumns()
  1278. /**
  1279. * checks if the columns which are given as the array's indexes really exist
  1280. * if not it will be unset anyway
  1281. *
  1282. * @version 2002/04/16
  1283. * @access public
  1284. * @author Wolfram Kriesing <wk@visionp.de>
  1285. * @param string the actual message, first word should always be the method name,
  1286. * to build the message like this: className::methodname
  1287. * @param integer the line number
  1288. */
  1289. function _checkColumns($newData, $method='unknown')
  1290. {
  1291. if (!$meta = $this->metadata()) { // if no metadata available, return data as given
  1292. return $newData;
  1293. }
  1294. foreach ($newData as $colName => $x) {
  1295. if (!isset($meta[$colName])) {
  1296. $this->_errorLog("$method, column {$this->table}.$colName doesnt exist, value was removed before '$method'",__LINE__);
  1297. unset($newData[$colName]);
  1298. } else {
  1299. // if the current column exists, check the length too, not to write content that is too long
  1300. // prevent DB-errors here
  1301. // do only check the data length if this field is given
  1302. if (isset($meta[$colName]['len']) && ($meta[$colName]['len'] != -1) &&
  1303. ($oldLength=strlen($newData[$colName])) > $meta[$colName]['len']
  1304. ) {
  1305. $this->_errorLog("_checkColumns, had to trim column '$colName' from $oldLength to ".
  1306. $meta[$colName]['DATA_LENGTH'].' characters.', __LINE__);
  1307. $newData[$colName] = substr($newData[$colName], 0, $meta[$colName]['len']);
  1308. }
  1309. }
  1310. }
  1311. return $newData;
  1312. }
  1313. // }}}
  1314. // {{{ debug()
  1315. /**
  1316. * overwrite this method and i.e. print the query $string
  1317. * to see the final query
  1318. *
  1319. * @param string the query mostly
  1320. */
  1321. function debug($string){}
  1322. //
  1323. //
  1324. // ONLY ORACLE SPECIFIC, not very nice since it is DB dependent, but we need it!!!
  1325. //
  1326. //
  1327. // }}}
  1328. // {{{ metadata()
  1329. /**
  1330. * !!!! query COPIED FROM db_oci8.inc - from PHPLIB !!!!
  1331. *
  1332. * @access public
  1333. * @see
  1334. * @version 2001/09
  1335. * @author PHPLIB
  1336. * @param
  1337. * @return
  1338. */
  1339. function metadata($table='')
  1340. {
  1341. // is there an alias in the table name, then we have something like this: 'user ua'
  1342. // cut of the alias and return the table name
  1343. if (strpos($table, ' ') !== false) {
  1344. $split = explode(' ', trim($table));
  1345. $table = $split[0];
  1346. }
  1347. $full = false;
  1348. if (empty($table)) {
  1349. $table = $this->table;
  1350. }
  1351. // to prevent multiple selects for the same metadata
  1352. if (isset($this->_metadata[$table])) {
  1353. return $this->_metadata[$table];
  1354. }
  1355. // FIXXXME use oci8 implementation of newer PEAR::DB-version
  1356. if ($this->db->phptype == 'oci8') {
  1357. $count = 0;
  1358. $id = 0;
  1359. $res = array();
  1360. //# This is a RIGHT OUTER JOIN: "(+)", if you want to see, what
  1361. //# this query results try the following:
  1362. //// $table = new Table; $this->db = new my_DB_Sql; // you have to make
  1363. //// // your own class
  1364. //// $table->show_results($this->db->query(see query vvvvvv))
  1365. ////
  1366. $res = $this->db->getAll("SELECT T.column_name,T.table_name,T.data_type,".
  1367. "T.data_length,T.data_precision,T.data_scale,T.nullable,".
  1368. "T.char_col_decl_length,I.index_name".
  1369. " FROM ALL_TAB_COLUMNS T,ALL_IND_COLUMNS I".
  1370. " WHERE T.column_name=I.column_name (+)".
  1371. " AND T.table_name=I.table_name (+)".
  1372. " AND T.table_name=UPPER('$table') ORDER BY T.column_id");
  1373. if (DB::isError($res)) {
  1374. //$this->_errorSet($res->getMessage());
  1375. // i think we only need to log here, since this method is never used
  1376. // directly for the user's functionality, which means if it fails it
  1377. // is most probably an appl error
  1378. $this->_errorLog($res->getUserInfo());
  1379. return false;
  1380. }
  1381. foreach ($res as $key=>$val) {
  1382. $res[$key]['name'] = $val['COLUMN_NAME'];
  1383. }
  1384. } else {
  1385. if (!is_object($this->db)) {
  1386. return false;
  1387. }
  1388. $res = $this->db->tableinfo($table);
  1389. if (DB::isError($res)) {
  1390. $this->_errorSet($res->getUserInfo());
  1391. return false;
  1392. }
  1393. }
  1394. $ret = array();
  1395. foreach ($res as $key => $val) {
  1396. $ret[$val['name']] = $val;
  1397. }
  1398. $this->_metadata[$table] = $ret;
  1399. return $ret;
  1400. }
  1401. //
  1402. // methods for building the query
  1403. //
  1404. // }}}
  1405. // {{{ _buildFrom()
  1406. /**
  1407. * build the from string
  1408. *
  1409. * @access private
  1410. * @return string the string added after FROM
  1411. */
  1412. function _buildFrom()
  1413. {
  1414. $from = $this->table;
  1415. $join = $this->getJoin();
  1416. if (!$join) { // no join set
  1417. return $from;
  1418. }
  1419. // handle the standard join thingy
  1420. if (isset($join['default']) && count($join['default'])) {
  1421. $from .= ','.implode(',',array_keys($join['default']));
  1422. }
  1423. // handle left/right joins
  1424. foreach (array('left', 'right') as $joinType) {
  1425. if (isset($join[$joinType]) && count($join[$joinType])) {
  1426. foreach($join[$joinType] as $table => $condition) {
  1427. // replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
  1428. // since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange
  1429. // FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column'
  1430. $regExp = '/_('.$table.')_([^\s]+)/';
  1431. $where = preg_replace($regExp, '$1.$2', $condition);
  1432. // add the table name before any column that has no table prefix
  1433. // since this might cause "unambiguous column" errors
  1434. if ($meta = $this->metadata()) {
  1435. foreach ($meta as $aCol=>$x) {
  1436. // this covers the LIKE,IN stuff: 'name LIKE "%you%"' 'id IN (2,3,4,5)'
  1437. $condition = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $condition);
  1438. // replace also the column names which are behind a '='
  1439. // and do this also if the aCol is at the end of the where clause
  1440. // that's what the $ is for
  1441. $condition = preg_replace('/=\s*'.$aCol.'(\s|$)/', "={$this->table}.$aCol ", $condition);
  1442. // replace if colName is first and possibly also if at the beginning of the where-string
  1443. $condition = preg_replace('/(^\s*|\s+)'.$aCol.'\s*=/', "$1{$this->table}.$aCol=", $condition);
  1444. }
  1445. }
  1446. $from .= ' '.strtoupper($joinType).' JOIN '.$table.' ON '.$condition;
  1447. }
  1448. }
  1449. }
  1450. return $from;
  1451. }
  1452. // }}}
  1453. // {{{ getTableShortName()
  1454. /**
  1455. * this method gets the short name for a table
  1456. *
  1457. * get the short name for a table, this is needed to properly build the
  1458. * 'AS' parts in the select query
  1459. * @param string the real table name
  1460. * @return string the table's short name
  1461. */
  1462. function getTableShortName($table)
  1463. {
  1464. $tableSpec = $this->getTableSpec(false);
  1465. if (isset($tableSpec[$table]['shortName']) && $tableSpec[$table]['shortName']) {
  1466. //print "$table ... ".$tableSpec[$table]['shortName'].'<br>';
  1467. return $tableSpec[$table]['shortName'];
  1468. }
  1469. $possibleTableShortName = preg_replace($this->_tableNameToShortNamePreg, '', $table);
  1470. //print "$table ... $possibleTableShortName<br>";
  1471. return $possibleTableShortName;
  1472. }
  1473. // }}}
  1474. // {{{ getTableSpec()
  1475. /**
  1476. * gets the tableSpec either indexed by the short name or the name
  1477. * returns the array for the tables given as parameter or if no
  1478. * parameter given for all tables that exist in the tableSpec
  1479. *
  1480. * @param array table names (not the short names!)
  1481. * @param boolean if true the table is returned indexed by the shortName
  1482. * otherwise indexed by the name
  1483. * @return array the tableSpec indexed
  1484. */
  1485. function getTableSpec($shortNameIndexed=true, $tables=array())
  1486. {
  1487. $newSpec = array();
  1488. foreach ($this->tableSpec as $aSpec) {
  1489. if (sizeof($tables)==0 || in_array($aSpec['name'],$tables)) {
  1490. if ($shortNameIndexed) {
  1491. $newSpec[$aSpec['shortName']] = $aSpec;
  1492. } else {
  1493. $newSpec[$aSpec['name']] = $aSpec;
  1494. }
  1495. }
  1496. }
  1497. return $newSpec;
  1498. }
  1499. // }}}
  1500. // {{{ _buildSelect()
  1501. /**
  1502. * build the 'SELECT <what> FROM ... 'for a select
  1503. *
  1504. * @version 2002/07/11
  1505. * @access public
  1506. * @author Wolfram Kriesing <wk@visionp.de>
  1507. * @param string if given use this string
  1508. * @return string the what-clause
  1509. */
  1510. function _buildSelect($what=null)
  1511. {
  1512. // what has preference, that means if what is set it is used
  1513. // this is only because the methods like 'get' pass an individually built value, which
  1514. // is supposed to be used, but usually it's generically build using the 'getSelect' values
  1515. if (empty($what) && $this->getSelect()) {
  1516. $what = $this->getSelect();
  1517. }
  1518. //
  1519. // replace all the '*' by the real column names, and take care of the dontSelect-columns!
  1520. //
  1521. $dontSelect = $this->getDontSelect();
  1522. $dontSelect = $dontSelect ? explode(',', $dontSelect) : array(); // make sure dontSelect is an array
  1523. // here we will replace all the '*' and 'table.*' by all the columns that this table
  1524. // contains. we do this so we can easily apply the 'dontSelect' values.
  1525. // and so we can also handle queries like: 'SELECT *,count() FROM ' and 'SELECT table.*,x FROM ' too
  1526. if (strpos($what, '*') !== false) {
  1527. // subpattern 1 get all the table names, that are written like this: 'table.*' including '*'
  1528. // for '*' the tablename will be ''
  1529. preg_match_all('/([^,]*)(\.)?\*\s*(,|$)/U', $what, $res);
  1530. //print "$what ... ";print_r($res);print "<br>";
  1531. $selectAllFromTables = array_unique($res[1]); // make the table names unique, so we do it all just once for each table
  1532. $tables = array();
  1533. if (in_array('', $selectAllFromTables)) { // was there a '*' ?
  1534. // get all the tables that we need to process, depending on if joined or not
  1535. $tables = $this->getJoin() ?
  1536. array_merge($this->getJoin('tables'), array($this->table)) : // get the joined tables and this->table
  1537. array($this->table); // create an array with only this->table
  1538. } else {
  1539. $tables = $selectAllFromTables;
  1540. }
  1541. $cols = array();
  1542. foreach ($tables as $aTable) { // go thru all the tables and get all columns for each, and handle 'dontSelect'
  1543. if ($meta = $this->metadata($aTable)) {
  1544. foreach ($meta as $colName => $x) {
  1545. // handle the dontSelect's
  1546. if (in_array($colName, $dontSelect) || in_array("$aTable.$colName", $dontSelect)) {
  1547. continue;
  1548. }
  1549. // build the AS clauses
  1550. // put " around them to enable use of reserved words, i.e. SELECT table.option as option FROM...
  1551. // and 'option' actually is a reserved word, at least in mysql
  1552. // put double quotes around them, since pgsql doesnt work with single quotes
  1553. // but don't do this for ibase because it doesn't work!
  1554. if ($aTable == $this->table) {
  1555. if ($this->db->phptype == 'ibase') {
  1556. $cols[$aTable][] = $this->table. '.' .$colName . ' AS '. $colName;
  1557. } else {
  1558. $cols[$aTable][] = $this->table. '.' .$colName . ' AS "'. $colName .'"';
  1559. }
  1560. } else {
  1561. $cols[$aTable][] = "$aTable.$colName AS \"_".$this->getTableShortName($aTable)."_$colName\"";
  1562. }
  1563. }
  1564. }
  1565. }
  1566. // put the extracted select back in the $what
  1567. // that means replace 'table.*' by the i.e. 'table.id AS _table_id'
  1568. // or if it is the table of this class replace 'table.id AS id'
  1569. if (in_array('', $selectAllFromTables)) {
  1570. $allCols = array();
  1571. foreach ($cols as $aTable) {
  1572. $allCols[] = implode(',', $aTable);
  1573. }
  1574. $what = preg_replace('/(^|,)\*($|,)/', '$1'.implode(',',$allCols).'$2', $what);
  1575. // remove all the 'table.*' since we have selected all anyway (because there was a '*' in the select)
  1576. $what = preg_replace('/[^,]*(\.)?\*\s*(,|$)/U', '', $what);
  1577. } else {
  1578. foreach ($cols as $tableName => $aTable) {
  1579. if (is_array($aTable) && sizeof($aTable)) {
  1580. // replace all the 'table.*' by their select of each column
  1581. $what = preg_replace('/(^|,)\s*'.$tableName.'\.\*\s*($|,)/', '$1'.implode(',',$aTable).'$2', $what);
  1582. }
  1583. }
  1584. }
  1585. }
  1586. if ($this->getJoin()) {
  1587. // replace all 'column' by '$this->table.column' to prevent ambigious errors
  1588. $metadata = $this->metadata();
  1589. if (is_array($metadata)) {
  1590. foreach ($metadata as $aCol => $x) {
  1591. // handle ',id as xid,MAX(id),id' etc.
  1592. // FIXXME do this better!!!
  1593. $what = preg_replace( "/(^|,|\()(\s*)$aCol(\)|\s|,|as|$)/i",
  1594. // $2 is actually just to keep the spaces, is not really
  1595. // necessary, but this way the test works independent of this functionality here
  1596. "$1$2{$this->table}.$aCol$3",
  1597. $what);
  1598. }
  1599. }
  1600. // replace all 'joinedTable.columnName' by '_joinedTable_columnName'
  1601. // this actually only has an effect if there was no 'table.*' for 'table'
  1602. // if that was there, then it has already been done before
  1603. foreach ($this->getJoin('tables') as $aTable) {
  1604. if ($meta = $this->metadata($aTable)) {
  1605. foreach ($meta as $aCol=>$x) {
  1606. // dont put the 'AS' behind it if there is already one
  1607. if (preg_match("/$aTable.$aCol\s*as/i",$what)) {
  1608. continue;
  1609. }
  1610. // this covers a ' table.colName ' surrounded by spaces, and replaces it by ' table.colName AS _table_colName'
  1611. $what = preg_replace('/\s'.$aTable.'.'.$aCol.'\s/', " $aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol ", $what);
  1612. // replace also the column names which are behind a ','
  1613. // and do this also if the aCol is at the end that's what the $ is for
  1614. $what = preg_replace('/,\s*'.$aTable.'.'.$aCol.'(,|\s|$)/', ",$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol$1", $what);
  1615. // replace if colName is first and possibly also if at the beginning of the where-string
  1616. $what = preg_replace('/(^\s*|\s+)'.$aTable.'.'.$aCol.'\s*,/', "$1$aTable.$aCol AS _".$this->getTableShortName($aTable)."_$aCol,", $what);
  1617. }
  1618. }
  1619. }
  1620. }
  1621. return $what;
  1622. }
  1623. // }}}
  1624. // {{{ _buildWhere()
  1625. /**
  1626. * Build WHERE clause
  1627. *
  1628. * @param string $where WHERE clause
  1629. * @return string $where WHERE clause after processing
  1630. * @access private
  1631. */
  1632. function _buildWhere($where='')
  1633. {
  1634. $where = trim($where);
  1635. $originalWhere = $this->getWhere();
  1636. if ($originalWhere) {
  1637. if (!empty($where)) {
  1638. $where = $originalWhere.' AND '.$where;
  1639. } else {
  1640. $where = $originalWhere;
  1641. }
  1642. }
  1643. $where = trim($where);
  1644. if ($join = $this->getJoin()) { // is join set?
  1645. // only those where conditions in the default-join have to be added here
  1646. // left-join conditions are added behind 'ON', the '_buildJoin()' does that
  1647. if (isset($join['default']) && count($join['default'])) {
  1648. // we have to add this join-where clause here
  1649. // since at least in mysql a query like: select * from tableX JOIN tableY ON ...
  1650. // doesnt work, may be that's even SQL-standard...
  1651. if (!empty($where)) {
  1652. $where = implode(' AND ', $join['default']).' AND '.$where;
  1653. } else {
  1654. $where = implode(' AND ', $join['default']);
  1655. }
  1656. }
  1657. // replace the _TABLENAME_COLUMNNAME by TABLENAME.COLUMNNAME
  1658. // since oracle doesnt work with the _TABLENAME_COLUMNNAME which i think is strange
  1659. // FIXXME i think this should become deprecated since the setWhere should not be used like this: '_table_column' but 'table.column'
  1660. $regExp = '/_('.implode('|', $this->getJoin('tables')).')_([^\s]+)/';
  1661. $where = preg_replace($regExp, '$1.$2', $where);
  1662. // add the table name before any column that has no table prefix
  1663. // since this might cause "unambigious column" errors
  1664. if ($meta = $this->metadata()) {
  1665. foreach ($meta as $aCol => $x) {
  1666. // this covers the LIKE,IN stuff: 'name LIKE "%you%"' 'id IN (2,3,4,5)'
  1667. $where = preg_replace('/\s'.$aCol.'\s/', " {$this->table}.$aCol ", $where);
  1668. // replace also the column names which are behind a '='
  1669. // and do this also if the aCol is at the end of the where clause
  1670. // that's what the $ is for
  1671. $where = preg_replace('/([=<>])\s*'.$aCol.'(\s|$)/', "$1{$this->table}.$aCol ", $where);
  1672. // replace if colName is first and possibly also if at the beginning of the where-string
  1673. $where = preg_replace('/(^\s*|\s+)'.$aCol.'\s*([=<>])/', "$1{$this->table}.$aCol$2", $where);
  1674. }
  1675. }
  1676. }
  1677. return $where;
  1678. }
  1679. // }}}
  1680. // {{{ _buildOrder()
  1681. /**
  1682. *
  1683. *
  1684. * @version 2002/07/11
  1685. * @access public
  1686. * @author Wolfram Kriesing <wk@visionp.de>
  1687. * @param
  1688. * @return
  1689. */
  1690. function _buildOrder()
  1691. {
  1692. $order = $this->getOrder();
  1693. // replace 'column' by '$this->table.column' if the column is defined for $this->table
  1694. if ($meta = $this->metadata()) {
  1695. foreach ($meta as $aCol=>$x) {
  1696. $order = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $order);
  1697. }
  1698. }
  1699. return $order;
  1700. }
  1701. // }}}
  1702. // {{{ _buildGroup()
  1703. /**
  1704. * Build the group-clause, replace 'column' by 'table.column'.
  1705. *
  1706. * @access public
  1707. * @param void
  1708. * @return string the rendered group clause
  1709. */
  1710. function _buildGroup()
  1711. {
  1712. $group = $this->getGroup();
  1713. // replace 'column' by '$this->table.column' if the column is defined for $this->table
  1714. if ($meta = $this->metadata()) {
  1715. foreach ($meta as $aCol => $x) {
  1716. $group = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U', "$1{$this->table}.$aCol$2", $group);
  1717. }
  1718. }
  1719. return $group;
  1720. }
  1721. // }}}
  1722. // {{{ _buildHaving()
  1723. /**
  1724. *
  1725. * @version 2003/06/05
  1726. * @access public
  1727. * @author Johannes Schaefer <johnschaefer@gmx.de>
  1728. * @param
  1729. * @return string the having clause
  1730. */
  1731. function _buildHaving()
  1732. {
  1733. $having = $this->getHaving();
  1734. // replace 'column' by '$this->table.column' if the column is defined for $this->table
  1735. if ($meta = $this->metadata()) {
  1736. foreach ($meta as $aCol => $x) {
  1737. $having = preg_replace('/(^\s*|\s+|,)'.$aCol.'\s*(,)?/U',"$1{$this->table}.$aCol$2",$having);
  1738. }
  1739. }
  1740. return $having;
  1741. }
  1742. // }}}
  1743. // {{{ _buildHaving()
  1744. /**
  1745. *
  1746. * @version 2002/07/11
  1747. * @access public
  1748. * @author Wolfram Kriesing <wk@visionp.de>
  1749. * @param array this array contains the elements of the query,
  1750. * indexed by their key, which are: 'select','from','where', etc.
  1751. * @param boolean whether this method is called via getCount() or not.
  1752. * @return
  1753. */
  1754. function _buildSelectQuery($query=array(), $isCalledViaGetCount = false)
  1755. {
  1756. /*FIXXXME finish this
  1757. $cacheKey = md5(serialize(????));
  1758. if (isset($this->_queryCache[$cacheKey])) {
  1759. $this->_errorLog('using cached query',__LINE__);
  1760. return $this->_queryCache[$cacheKey];
  1761. }
  1762. */
  1763. $where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
  1764. if ($where) {
  1765. $where = 'WHERE '.$where;
  1766. }
  1767. $order = isset($query['order']) ? $query['order'] : $this->_buildOrder();
  1768. if ($order) {
  1769. $order = 'ORDER BY '.$order;
  1770. }
  1771. $group = isset($query['group']) ? $query['group'] : $this->_buildGroup();
  1772. if ($group) {
  1773. $group = 'GROUP BY '.$group;
  1774. }
  1775. $having = isset($query['having']) ? $query['having'] : $this->_buildHaving();
  1776. if ($having) {
  1777. $having = 'HAVING '.$having;
  1778. }
  1779. $queryString = sprintf( 'SELECT %s FROM %s %s %s %s %s',
  1780. isset($query['select']) ? $query['select'] : $this->_buildSelect(),
  1781. isset($query['from']) ? $query['from'] : $this->_buildFrom(),
  1782. $where,
  1783. $group,
  1784. $having,
  1785. $order
  1786. );
  1787. // $query['limit'] has preference!
  1788. $limit = isset($query['limit']) ? $query['limit'] : $this->_limit;
  1789. if (!$isCalledViaGetCount && @$limit[1]) { // is there a count set?
  1790. $queryString=$this->db->modifyLimitQuery($queryString,$limit[0],$limit[1]);
  1791. if (DB::isError($queryString)) {
  1792. $this->_errorSet('DB_QueryTool::db::modifyLimitQuery failed '.$queryString->getMessage());
  1793. $this->_errorLog($queryString->getUserInfo());
  1794. return false;
  1795. }
  1796. }
  1797. // $this->_queryCache[$cacheKey] = $queryString;
  1798. return $queryString;
  1799. }
  1800. // }}}
  1801. // {{{ _buildUpdateQuery()
  1802. /**
  1803. * this simply builds an update query.
  1804. *
  1805. * @param array the parameter array might contain the following indexes
  1806. * 'where' the where clause to be added, i.e.
  1807. * UPDATE table SET x=1 WHERE y=0
  1808. * here the 'where' part simply would be 'y=0'
  1809. * 'set' the actual data to be updated
  1810. * in the example above, that would be 'x=1'
  1811. * @return string the resulting query
  1812. */
  1813. function _buildUpdateQuery($query=array())
  1814. {
  1815. $where = isset($query['where']) ? $query['where'] : $this->_buildWhere();
  1816. if ($where) {
  1817. $where = 'WHERE '.$where;
  1818. }
  1819. $updateString = sprintf('UPDATE %s SET %s %s',
  1820. $this->table,
  1821. $query['set'],
  1822. $where
  1823. );
  1824. return $updateString;
  1825. }
  1826. // }}}
  1827. // {{{ execute()
  1828. /**
  1829. *
  1830. * @version 2002/07/11
  1831. * @access public
  1832. * @author Wolfram Kriesing <wk@visionp.de>
  1833. * @param
  1834. * @param string query method
  1835. * @return boolean
  1836. */
  1837. function execute($query=null, $method='getAll')
  1838. {
  1839. $this->writeLog();
  1840. if (is_null($query)) {
  1841. $query = $this->_buildSelectQuery();
  1842. }
  1843. $this->writeLog('query built: '.$query);
  1844. // FIXXME on ORACLE this doesnt work, since we return joined columns as _TABLE_COLNAME and the _ in front
  1845. // doesnt work on oracle, add a letter before it!!!
  1846. $this->_lastQuery = $query;
  1847. $this->debug($query);
  1848. $this->writeLog('start query');
  1849. if (DB::isError($res = $this->db->$method($query))) {
  1850. $this->writeLog('end query (failed)');
  1851. if ($this->getOption('verbose')) {
  1852. $this->_errorSet($res->getMessage());
  1853. } else {
  1854. $this->_errorLog($res->getMessage());
  1855. }
  1856. $this->_errorLog($res->getUserInfo(), __LINE__);
  1857. return false;
  1858. } else {
  1859. $this->writeLog('end query');
  1860. }
  1861. $res = $this->_makeIndexed($res);
  1862. return $res;
  1863. }
  1864. // }}}
  1865. // {{{ writeLog()
  1866. /**
  1867. * Write events to the logfile.
  1868. *
  1869. * It does some additional work, like time measuring etc. to
  1870. * see some additional info
  1871. *
  1872. */
  1873. function writeLog($text='START')
  1874. {
  1875. //its still really a quicky.... 'refactor' (nice word) that
  1876. if (!isset($this->options['logfile'])) {
  1877. return;
  1878. }
  1879. include_once 'Log.php';
  1880. if (!class_exists('Log')) {
  1881. return;
  1882. }
  1883. if (!$this->_logObject) {
  1884. $this->_logObject =& Log::factory('file', $this->options['logfile']);
  1885. }
  1886. if ($text==='start query' || $text==='end query') {
  1887. $bytesSent = $this->db->getAll("SHOW STATUS like 'Bytes_sent'");
  1888. $bytesSent = $bytesSent[0]['Value'];
  1889. }
  1890. if ($text==='START') {
  1891. $startTime = split(' ', microtime());
  1892. $this->_logData['startTime'] = $startTime[1] + $startTime[0];
  1893. }
  1894. if ($text==='start query') {
  1895. $this->_logData['startBytesSent'] = $bytesSent;
  1896. $startTime = split(' ', microtime());
  1897. $this->_logData['startQueryTime'] = $startTime[1] + $startTime[0];
  1898. return;
  1899. }
  1900. if ($text==='end query') {
  1901. $text .= ' result size: '.((int)$bytesSent-(int)$this->_logData['startBytesSent']).' bytes';
  1902. $endTime = split(' ', microtime());
  1903. $endTime = $endTime[1] + $endTime[0];
  1904. $text .= ', took: '.(($endTime - $this->_logData['startQueryTime'])).' seconds';
  1905. }
  1906. if (strpos($text, 'query built')===0) {
  1907. $endTime = split(' ', microtime());
  1908. $endTime = $endTime[1] + $endTime[0];
  1909. $this->writeLog('query building took: '.(($endTime - $this->_logData['startTime'])).' seconds');
  1910. }
  1911. $this->_logObject->log($text);
  1912. if (strpos($text, 'end query')===0) {
  1913. $endTime = split(' ', microtime());
  1914. $endTime = $endTime[1] + $endTime[0];
  1915. $text = 'time over all: '.(($endTime - $this->_logData['startTime'])).' seconds';
  1916. $this->_logObject->log($text);
  1917. }
  1918. }
  1919. // }}}
  1920. // {{{ returnResult()
  1921. /**
  1922. * Return the chosen result type
  1923. *
  1924. * @version 2004/04/28
  1925. * @access public
  1926. * @param object reference
  1927. * @return mixed
  1928. */
  1929. function returnResult(&$result)
  1930. {
  1931. if ($this->_resultType == 'none') {
  1932. return $result;
  1933. }
  1934. if ($result === false) {
  1935. return false;
  1936. }
  1937. //what about allowing other (custom) result types?
  1938. switch (strtolower($this->_resultType)) {
  1939. case 'object': return new DB_QueryTool_Result_Object($result);
  1940. case 'array':
  1941. default: return new DB_QueryTool_Result($result);
  1942. }
  1943. }
  1944. // }}}
  1945. // {{{ _makeIndexed()
  1946. /**
  1947. *
  1948. * @version 2002/07/11
  1949. * @access public
  1950. * @author Wolfram Kriesing <wk@visionp.de>
  1951. * @param mixed
  1952. * @return mixed
  1953. */
  1954. function &_makeIndexed(&$data)
  1955. {
  1956. // we can only return an indexed result if the result has a number of columns
  1957. if (is_array($data) && sizeof($data) && $key = $this->getIndex()) {
  1958. // build the string to evaluate which might be made up out of multiple indexes of a result-row
  1959. $evalString = '$val[\''.implode('\'].\',\'.$val[\'',explode(',',$key)).'\']'; //"
  1960. $indexedData = array();
  1961. //FIXXME actually we also need to check ONCE if $val is an array, so to say if $data is 2-dimensional
  1962. foreach ($data as $val) {
  1963. eval("\$keyValue = $evalString;"); // get the actual real (string-)key (string if multiple cols are used as index)
  1964. $indexedData[$keyValue] = $val;
  1965. }
  1966. unset($data);
  1967. return $indexedData;
  1968. }
  1969. return $data;
  1970. }
  1971. // }}}
  1972. // {{{ setIndex()
  1973. /**
  1974. * format the result to be indexed by $key
  1975. * NOTE: be careful, when using this you should be aware, that if you
  1976. * use an index which's value appears multiple times you may loose data
  1977. * since a key cant exist multiple times!!
  1978. * the result for a result to be indexed by a key(=columnName)
  1979. * (i.e. 'relationtoMe') which's values are 'brother' and 'sister'
  1980. * or alike normally returns this:
  1981. * $res['brother'] = array('name'=>'xxx')
  1982. * $res['sister'] = array('name'=>'xxx')
  1983. * but if the column 'relationtoMe' contains multiple entries for 'brother'
  1984. * then the returned dataset will only contain one brother, since the
  1985. * value from the column 'relationtoMe' is used
  1986. * and which 'brother' you get depends on a lot of things, like the sortorder,
  1987. * how the db saves the data, and whatever else
  1988. *
  1989. * you can also set indexes which depend on 2 columns, simply pass the parameters like
  1990. * 'table1.id,table2.id' it will be used as a string for indexing the result
  1991. * and the index will be built using the 2 values given, so a possible
  1992. * index might be '1,2' or '2108,29389' this way you can access data which
  1993. * have 2 primary keys. Be sure to remember that the index is a string!
  1994. *
  1995. * @version 2002/07/11
  1996. * @access public
  1997. * @author Wolfram Kriesing <wk@visionp.de>
  1998. * @param
  1999. * @return
  2000. */
  2001. function setIndex($key=null)
  2002. {
  2003. if ($this->getJoin()) { // is join set?
  2004. // replace TABLENAME.COLUMNNAME by _TABLENAME_COLUMNNAME
  2005. // since this is only the result-keys can be used for indexing :-)
  2006. $regExp = '/('.implode('|', $this->getJoin('tables')).')\.([^\s]+)/';
  2007. $key = preg_replace($regExp, '_$1_$2', $key);
  2008. // remove the table name if it is in front of '<$this->table>.columnname'
  2009. // since the key doesnt contain it neither
  2010. if ($meta = $this->metadata()) {
  2011. foreach ($meta as $aCol => $x) {
  2012. $key = preg_replace('/'.$this->table.'\.'.$aCol.'/', $aCol, $key);
  2013. }
  2014. }
  2015. }
  2016. $this->_index = $key;
  2017. }
  2018. // }}}
  2019. // {{{ getIndex()
  2020. /**
  2021. *
  2022. * @version 2002/07/11
  2023. * @access public
  2024. * @author Wolfram Kriesing <wk@visionp.de>
  2025. * @param
  2026. * @return
  2027. */
  2028. function getIndex()
  2029. {
  2030. return $this->_index;
  2031. }
  2032. // }}}
  2033. // {{{ useResult()
  2034. /**
  2035. * Choose the type of the returned result
  2036. *
  2037. * @version 2004/04/28
  2038. * @access public
  2039. * @param string $type ['array' | 'object' | 'none']
  2040. * For BC reasons, $type=true is equal to 'array',
  2041. * $type=false is equal to 'none'
  2042. */
  2043. function useResult($type='array')
  2044. {
  2045. if ($type === true) {
  2046. $type = 'array';
  2047. } elseif ($type === false) {
  2048. $type = 'none';
  2049. }
  2050. switch (strtolower($type)) {
  2051. case 'array':
  2052. $this->_resultType = 'array';
  2053. require_once 'DB/QueryTool/Result.php';
  2054. break;
  2055. case 'object':
  2056. $this->_resultType = 'object';
  2057. require_once 'DB/QueryTool/Result/Object.php';
  2058. break;
  2059. default:
  2060. $this->_resultType = 'none';
  2061. }
  2062. }
  2063. // }}}
  2064. // {{{ setErrorCallback()
  2065. /**
  2066. * set both callbacks
  2067. * @param string
  2068. */
  2069. function setErrorCallback($param='')
  2070. {
  2071. $this->setErrorLogCallback($param);
  2072. $this->setErrorSetCallback($param);
  2073. }
  2074. // }}}
  2075. // {{{ setErrorLogCallback()
  2076. /**
  2077. * @param string
  2078. */
  2079. function setErrorLogCallback($param='')
  2080. {
  2081. $errorLogCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorLogCallback');
  2082. $errorLogCallback = $param;
  2083. }
  2084. // }}}
  2085. // {{{ setErrorSetCallback()
  2086. /**
  2087. * @param string
  2088. */
  2089. function setErrorSetCallback($param='')
  2090. {
  2091. $errorSetCallback = &PEAR::getStaticProperty('DB_QueryTool','_errorSetCallback');
  2092. $errorSetCallback = $param;
  2093. }
  2094. // }}}
  2095. // {{{ _errorLog()
  2096. /**
  2097. * sets error log and adds additional info
  2098. *
  2099. * @version 2002/04/16
  2100. * @access public
  2101. * @author Wolfram Kriesing <wk@visionp.de>
  2102. * @param string the actual message, first word should always be the method name,
  2103. * to build the message like this: className::methodname
  2104. * @param integer the line number
  2105. */
  2106. function _errorLog($msg, $line='unknown')
  2107. {
  2108. $this->_errorHandler('log', $msg, $line);
  2109. /*
  2110. if ($this->getOption('verbose') == true)
  2111. {
  2112. $this->_errorLog(get_class($this)."::$msg ($line)");
  2113. return;
  2114. }
  2115. if ($this->_errorLogCallback)
  2116. call_user_func($this->_errorLogCallback, $msg);
  2117. */
  2118. }
  2119. // }}}
  2120. // {{{ _errorSet()
  2121. /**
  2122. * @param string
  2123. * @param string
  2124. */
  2125. function _errorSet($msg, $line='unknown')
  2126. {
  2127. $this->_errorHandler('set', $msg, $line);
  2128. }
  2129. // }}}
  2130. // {{{ _errorHandler()
  2131. /**
  2132. * @param
  2133. * @param string
  2134. * @param string
  2135. */
  2136. function _errorHandler($logOrSet, $msg, $line='unknown')
  2137. {
  2138. /* what did i do this for?
  2139. if ($this->getOption('verbose') == true)
  2140. {
  2141. $this->_errorHandler($logOrSet, get_class($this)."::$msg ($line)");
  2142. return;
  2143. }
  2144. */
  2145. $msg = get_class($this)."::$msg ($line)";
  2146. $logOrSet = ucfirst($logOrSet);
  2147. $callback = &PEAR::getStaticProperty('DB_QueryTool','_error'.$logOrSet.'Callback');
  2148. //var_dump($callback);
  2149. //if ($callback)
  2150. // call_user_func($callback, $msg);
  2151. // else
  2152. // ?????
  2153. }
  2154. // }}}
  2155. }
  2156. ?>