95204df18211cb771043c036ac8c61b60fc3c2d5
[Packages/TYPO3.CMS.git] / typo3 / sysext / dbal / Classes / Database / AdodbPreparedStatement.php
1 <?php
2 namespace TYPO3\CMS\Dbal\Database;
3
4 /**
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 use TYPO3\CMS\Core\Utility\GeneralUtility;
18
19 /**
20 * MySQLi Prepared Statement-compatible implementation for ADOdb.
21 *
22 * Notice: Extends \TYPO3\CMS\Dbal\Database\DatabaseConnection to be able to access
23 * protected properties solely (thus would be a "friend" class in C++).
24 *
25 * @author Xavier Perseguers <xavier@typo3.org>
26 */
27 class AdodbPreparedStatement extends \TYPO3\CMS\Dbal\Database\DatabaseConnection {
28
29 /**
30 * @var \TYPO3\CMS\Dbal\Database\DatabaseConnection
31 */
32 protected $databaseConnection;
33
34 /**
35 * @var string
36 */
37 protected $query;
38
39 /**
40 * @var array
41 */
42 protected $queryComponents;
43
44 /**
45 * @var array
46 */
47 protected $parameters;
48
49 /**
50 * @var \ADORecordSet_array
51 */
52 protected $recordSet;
53
54 /**
55 * Default constructor.
56 *
57 * @param string $query
58 * @param array $queryComponents
59 * @param \TYPO3\CMS\Dbal\Database\DatabaseConnection $databaseConnection
60 */
61 public function __construct($query, array $queryComponents, \TYPO3\CMS\Dbal\Database\DatabaseConnection $databaseConnection) {
62 $this->databaseConnection = $databaseConnection;
63 $this->query = $query;
64 $this->queryComponents = $queryComponents;
65 $this->parameters = array();
66 }
67
68 /**
69 * Prepares an SQL statement for execution.
70 *
71 * @return bool TRUE on success or FALSE on failure
72 */
73 public function prepare() {
74 // TODO: actually prepare the query with ADOdb, if supported by the underlying DBMS
75 // see: http://phplens.com/lens/adodb/docs-adodb.htm#prepare
76 return TRUE;
77 }
78
79 /**
80 * Transfers a result set from a prepared statement.
81 *
82 * @return TRUE on success or FALSE on failure
83 */
84 public function store_result() {
85 return TRUE;
86 }
87
88 /**
89 * Binds variables to a prepared statement as parameters.
90 *
91 * @param string $types
92 * @param mixed $var1 The number of variables and length of string types must match the parameters in the statement.
93 * @param mixed $_ [optional]
94 * @return bool TRUE on success or FALSE on failure
95 * @see \mysqli_stmt::bind_param()
96 */
97 public function bind_param($types, $var1, $_ = NULL) {
98 $numberOfVariables = strlen($types);
99 if (func_num_args() !== $numberOfVariables + 1) {
100 return FALSE;
101 }
102
103 $this->parameters = array(
104 array(
105 'type' => $types{0},
106 'value' => $var1
107 ),
108 );
109 for ($i = 1; $i < $numberOfVariables; $i++) {
110 $this->parameters[] = array(
111 'type' => $types{$i},
112 'value' => func_get_arg($i + 1),
113 );
114 }
115
116 return TRUE;
117 }
118
119 /**
120 * Resets a prepared statement.
121 *
122 * @return bool TRUE on success or FALSE on failure
123 */
124 public function reset() {
125 return TRUE;
126 }
127
128 /**
129 * Executes a prepared query.
130 *
131 * @return bool TRUE on success or FALSE on failure
132 */
133 public function execute() {
134 $queryParts = $this->queryComponents['queryParts'];
135 $numberOfParameters = count($this->parameters);
136 for ($i = 0; $i < $numberOfParameters; $i++) {
137 $value = $this->parameters[$i]['value'];
138 switch ($this->parameters[$i]['type']) {
139 case 's':
140 if ($value !== NULL) {
141 $value = $this->databaseConnection->fullQuoteStr($value, $this->queryComponents['ORIG_tableName']);
142 }
143 break;
144 case 'i':
145 $value = (int)$value;
146 break;
147 default:
148 // Same error as in \TYPO3\CMS\Core\Database\PreparedStatement::execute()
149 throw new \InvalidArgumentException(sprintf('Unknown type %s used for parameter %s.', $this->parameters[$i]['type'], $i + 1), 1281859196);
150 }
151
152 $queryParts[$i * 2 + 1] = $value;
153 }
154
155 // Standard query from now on
156 $query = implode('', $queryParts);
157
158 $limit = $this->queryComponents['LIMIT'];
159 if ($this->databaseConnection->runningADOdbDriver('postgres')) {
160 // Possibly rewrite the LIMIT to be PostgreSQL-compatible
161 $splitLimit = GeneralUtility::intExplode(',', $limit);
162 // Splitting the limit values:
163 if ($splitLimit[1]) {
164 // If there are two parameters, do mapping differently than otherwise:
165 $numRows = $splitLimit[1];
166 $offset = $splitLimit[0];
167 $limit = $numrows . ' OFFSET ' . $offset;
168 }
169 }
170 if ($limit !== '') {
171 $splitLimit = GeneralUtility::intExplode(',', $limit);
172 // Splitting the limit values:
173 if ($splitLimit[1]) {
174 // If there are two parameters, do mapping differently than otherwise:
175 $numRows = $splitLimit[1];
176 $offset = $splitLimit[0];
177 } else {
178 $numRows = $splitLimit[0];
179 $offset = 0;
180 }
181 $this->recordSet = $this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->SelectLimit($query, $numRows, $offset);
182 $this->databaseConnection->lastQuery = $this->recordSet->sql;
183 } else {
184 $this->databaseConnection->lastQuery = $query;
185 $this->recordSet = $this->databaseConnection->handlerInstance[$this->databaseConnection->lastHandlerKey]->_Execute($this->databaseConnection->lastQuery);
186 }
187
188 if ($this->recordSet !== FALSE) {
189 $success = TRUE;
190 $this->recordSet->TYPO3_DBAL_handlerType = 'adodb';
191 // Setting handler type in result object (for later recognition!)
192 //$this->recordSet->TYPO3_DBAL_tableList = $queryComponents['ORIG_tableName'];
193 } else {
194 $success = FALSE;
195 }
196
197 return $success;
198 }
199
200 /**
201 * Returns an array of objects representing the fields in a result set.
202 *
203 * @return array
204 */
205 public function fetch_fields() {
206 return $this->recordSet !== FALSE ? $this->recordSet->_fieldobjects : array();
207 }
208
209 /**
210 * Fetches a row from the underlying result set.
211 *
212 * @return array Array of rows or FALSE if there are no more rows.
213 */
214 public function fetch() {
215 $row = $this->databaseConnection->sql_fetch_assoc($this->recordSet);
216 return $row;
217 }
218
219 /**
220 * Seeks to an arbitrary row in statement result set.
221 *
222 * @param int $offset Must be between zero and the total number of rows minus one
223 * @return bool TRUE on success or FALSE on failure
224 */
225 public function data_seek($offset) {
226 return $this->databaseConnection->sql_data_seek($this->recordSet, $offset);
227 }
228
229 /**
230 * Closes a prepared statement.
231 *
232 * @return bool TRUE on success or FALSE on failure
233 */
234 public function close() {
235 return $this->databaseConnection->sql_free_result($this->recordSet);
236 }
237
238 /**
239 * Magic getter for public properties of \mysqli_stmt access
240 * by \TYPO3\CMS\Core\Database\PreparedStatement.
241 *
242 * @param string $name
243 * @return mixed
244 */
245 public function __get($name) {
246 switch ($name) {
247 case 'errno':
248 $output = $this->databaseConnection->sql_errno();
249 break;
250 case 'error':
251 $output = $this->databaseConnection->sql_error();
252 break;
253 case 'num_rows':
254 $output = $this->databaseConnection->sql_num_rows($this->recordSet);
255 break;
256 default:
257 throw new \RuntimeException('Cannot access property ' . $name, 1394631927);
258 }
259 return $output;
260 }
261
262 }