1<?php 2/** 3 * This file contains the class XML_Query2XML_Driver_DB. 4 * 5 * PHP version 5 6 * 7 * @category XML 8 * @package XML_Query2XML 9 * @author Lukas Feiler <lukas.feiler@lukasfeiler.com> 10 * @copyright 2006 Lukas Feiler 11 * @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1 12 * @version CVS: $Id: DB.php 276639 2009-03-01 13:17:08Z lukasfeiler $ 13 * @link http://pear.php.net/package/XML_Query2XML 14 */ 15 16/** 17 * XML_Query2XML_Driver_DB extends XML_Query2XML_Driver. 18 */ 19require_once 'XML/Query2XML.php'; 20 21/** 22 * As the method PEAR::isError() is used within XML_Query2XML_Driver_DB 23 * we require PEAR.php. 24 */ 25require_once 'PEAR.php'; 26 27/** 28 * Driver for the database abstraction layer PEAR DB. 29 * 30 * usage: 31 * <code> 32 * $driver = XML_Query2XML_Driver::factory(DB::connect(...)); 33 * </code> 34 * 35 * @category XML 36 * @package XML_Query2XML 37 * @author Lukas Feiler <lukas.feiler@lukasfeiler.com> 38 * @copyright 2006 Lukas Feiler 39 * @license http://www.gnu.org/copyleft/lesser.html LGPL Version 2.1 40 * @version Release: 1.7.2 41 * @link http://pear.php.net/package/XML_Query2XML 42 * @since Release 1.5.0RC1 43 */ 44class XML_Query2XML_Driver_DB extends XML_Query2XML_Driver 45{ 46 /** 47 * In instance of a class that extends DB_common. 48 * @var DB_common 49 */ 50 private $_db = null; 51 52 /** 53 * Constructor 54 * 55 * @param DB_Common $db An instance of a class that extends DB_Common. 56 * 57 * @throws XML_Query2XML_DBException If the fetch mode cannot be set to 58 * DB_FETCHMODE_ASSOC. 59 */ 60 public function __construct(DB_common $db) 61 { 62 $fetchModeError = $db->setFetchMode(DB_FETCHMODE_ASSOC); 63 if (PEAR::isError($fetchModeError)) { 64 // no unit tests for this one 65 throw new XML_Query2XML_DBException( 66 'Could not set fetch mode to DB_FETCHMODE_ASSOC: ' 67 . $fetchModeError->toString() 68 ); 69 } 70 $this->_db = $db; 71 } 72 73 /** 74 * Pre-processes a query specification and returns a string representation 75 * of the query. 76 * This method will call parent::preprocessQuery(). Additionally it will 77 * verify $query['limit'] and $query['offset']. 78 * 79 * @param mixed &$query A string or an array containing the element 'query'. 80 * @param string $configPath The config path; used for exception messages. 81 * 82 * @return string The query statement as a string. 83 * @throws XML_Query2XML_ConfigException If $query['limit'] or $query['offset'] 84 * is set but not numeric. This exception 85 * might also bubble up from 86 * parent::preprocessQuery(). 87 */ 88 public function preprocessQuery(&$query, $configPath) 89 { 90 /* 91 * This will make $query an array if it is not already. 92 * We'll ignore preprocessQuery()'s return value here. 93 */ 94 parent::preprocessQuery($query, $configPath); 95 96 foreach (array('limit', 'offset') as $sqlOption) { 97 if (isset($query[$sqlOption])) { 98 if (!is_numeric($query[$sqlOption])) { 99 /* 100 * unit test: getXML/ 101 * offsetlimit_throwConfigException_limit_not_numeric.phpt 102 * offsetlimit_throwConfigException_offset_not_numeric.phpt 103 */ 104 throw new XML_Query2XML_ConfigException( 105 $configPath . '[' . $sqlOption 106 . ']: integer expected, ' 107 . gettype($query[$sqlOption]) . ' given.' 108 ); 109 } 110 } 111 } 112 $queryString = $query['query']; 113 if (isset($query['limit'])) { 114 if ($query['limit'] == 0) { 115 // setting limit to 0 is like not setting it at all 116 unset($query['limit']); 117 } else { 118 if (!isset($query['offset'])) { 119 // offset defaults to 0 120 $query['offset'] = 0; 121 } 122 $queryString .= '; LIMIT:' . $query['limit']; 123 $queryString .= '; OFFSET:' . $query['offset']; 124 125 $query['query'] = $this->_db->modifyLimitQuery( 126 $query['query'], 127 $query['offset'], 128 $query['limit'] 129 ); 130 } 131 } 132 return $queryString; 133 } 134 135 /** 136 * Execute a SQL SELECT stement and fetch all records from the result set. 137 * 138 * @param mixed $sql The SQL query as an array containing the 139 * element 'query'. 140 * @param string $configPath The config path; used for exception messages. 141 * 142 * @return array An array of records. 143 * @throws XML_Query2XML_DBException If a database related error occures. 144 * @see XML_Query2XML_Driver::getAllRecords() 145 */ 146 public function getAllRecords($sql, $configPath) 147 { 148 if (isset($sql['limit']) && $sql['limit'] < 0) { 149 return array(); 150 } 151 $result =& $this->_prepareAndExecute($sql, $configPath); 152 $records = array(); 153 while ($record = $result->fetchRow()) { 154 if (PEAR::isError($record)) { 155 // no unit test for this exception as it cannot be produced easily 156 throw new XML_Query2XML_DBException( 157 $configPath . ': Could not fetch rows for the following ' 158 . 'SQL query: ' . $sql['query'] . '; ' 159 . $record->toString() 160 ); 161 } 162 $records[] = $record; 163 } 164 $result->free(); 165 return $records; 166 } 167 168 /** 169 * Private method that will use DB_Common::prepare() & DB_Common::execute() 170 * to run an SQL query. 171 * 172 * @param mixed $sql An associative array with a 'query' element. 173 * @param string $configPath The config path used for exception messages. 174 * 175 * @return DB_result 176 * @throws XML_Query2XML_DBException If a database related error occures. 177 */ 178 private function _prepareAndExecute($sql, $configPath) 179 { 180 $query = $sql['query']; 181 182 if (isset($this->_preparedQueries[$query])) { 183 $queryHandle = $this->_preparedQueries[$query]; 184 } else { 185 // PREPARE 186 $queryHandle = $this->_db->prepare($query); 187 188 if (PEAR::isError($queryHandle)) { 189 /* 190 * No unit test for this exception as DB's mysql and pgsql 191 * drivers never return a PEAR error from prepare(). 192 */ 193 throw new XML_Query2XML_DBException( 194 $configPath . ': Could not prepare the following SQL query: ' 195 . $query . '; ' . $queryHandle->toString() 196 ); 197 } 198 199 $this->_preparedQueries[$query] =& $queryHandle; 200 } 201 202 // EXECUTE 203 if (isset($sql['data'])) { 204 $result = $this->_db->execute($queryHandle, $sql['data']); 205 } else { 206 $result = $this->_db->execute($queryHandle); 207 } 208 209 if (PEAR::isError($result)) { 210 /* 211 * unit test: DB/_prepareAndExecute/ 212 * throwDBException_complexQuery.phpt 213 */ 214 throw new XML_Query2XML_DBException( 215 $configPath . ': Could not execute the following SQL query: ' 216 . $query . '; ' . $result->toString() 217 ); 218 } 219 if (!($result instanceof DB_result)) { 220 /* 221 * unit tests: DB/getXML/ 222 * throwDBException_nullResultSet_complexQuery_multipleRecords.phpt 223 * throwDBException_nullResultSet_complexQuery_singleRecord.phpt 224 */ 225 throw new XML_Query2XML_DBException( 226 $configPath . ': the following SQL query returned no ' 227 . 'result set: ' . $query 228 ); 229 } 230 return $result; 231 } 232} 233?>