1<?php
2/**
3 * Zend Framework
4 *
5 * LICENSE
6 *
7 * This source file is subject to the new BSD license that is bundled
8 * with this package in the file LICENSE.txt.
9 * It is also available through the world-wide-web at this URL:
10 * http://framework.zend.com/license/new-bsd
11 * If you did not receive a copy of the license and are unable to
12 * obtain it through the world-wide-web, please send an email
13 * to license@zend.com so we can send you a copy immediately.
14 *
15 * @category   Zend
16 * @package    Zend_Db
17 * @subpackage Statement
18 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
19 * @license    http://framework.zend.com/license/new-bsd     New BSD License
20 * @version    $Id: Mysqli.php 23775 2011-03-01 17:25:24Z ralph $
21 */
22
23
24/**
25 * @see Zend_Db_Statement
26 */
27// require_once 'Zend/Db/Statement.php';
28
29
30/**
31 * Extends for Mysqli
32 *
33 * @category   Zend
34 * @package    Zend_Db
35 * @subpackage Statement
36 * @copyright  Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
37 * @license    http://framework.zend.com/license/new-bsd     New BSD License
38 */
39class Zend_Db_Statement_Mysqli extends Zend_Db_Statement
40{
41
42    /**
43     * Column names.
44     *
45     * @var array
46     */
47    protected $_keys;
48
49    /**
50     * Fetched result values.
51     *
52     * @var array
53     */
54    protected $_values;
55
56    /**
57     * @var array
58     */
59    protected $_meta = null;
60
61    /**
62     * @param  string $sql
63     * @return void
64     * @throws Zend_Db_Statement_Mysqli_Exception
65     */
66    public function _prepare($sql)
67    {
68        $mysqli = $this->_adapter->getConnection();
69
70        $this->_stmt = $mysqli->prepare($sql);
71
72        if ($this->_stmt === false || $mysqli->errno) {
73            /**
74             * @see Zend_Db_Statement_Mysqli_Exception
75             */
76            // require_once 'Zend/Db/Statement/Mysqli/Exception.php';
77            throw new Zend_Db_Statement_Mysqli_Exception("Mysqli prepare error: " . $mysqli->error, $mysqli->errno);
78        }
79    }
80
81    /**
82     * Binds a parameter to the specified variable name.
83     *
84     * @param mixed $parameter Name the parameter, either integer or string.
85     * @param mixed $variable  Reference to PHP variable containing the value.
86     * @param mixed $type      OPTIONAL Datatype of SQL parameter.
87     * @param mixed $length    OPTIONAL Length of SQL parameter.
88     * @param mixed $options   OPTIONAL Other options.
89     * @return bool
90     * @throws Zend_Db_Statement_Mysqli_Exception
91     */
92    protected function _bindParam($parameter, &$variable, $type = null, $length = null, $options = null)
93    {
94        return true;
95    }
96
97    /**
98     * Closes the cursor and the statement.
99     *
100     * @return bool
101     */
102    public function close()
103    {
104        if ($this->_stmt) {
105            $r = $this->_stmt->close();
106            $this->_stmt = null;
107            return $r;
108        }
109        return false;
110    }
111
112    /**
113     * Closes the cursor, allowing the statement to be executed again.
114     *
115     * @return bool
116     */
117    public function closeCursor()
118    {
119        if ($stmt = $this->_stmt) {
120            $mysqli = $this->_adapter->getConnection();
121            while ($mysqli->more_results()) {
122                $mysqli->next_result();
123            }
124            $this->_stmt->free_result();
125            return $this->_stmt->reset();
126        }
127        return false;
128    }
129
130    /**
131     * Returns the number of columns in the result set.
132     * Returns null if the statement has no result set metadata.
133     *
134     * @return int The number of columns.
135     */
136    public function columnCount()
137    {
138        if (isset($this->_meta) && $this->_meta) {
139            return $this->_meta->field_count;
140        }
141        return 0;
142    }
143
144    /**
145     * Retrieves the error code, if any, associated with the last operation on
146     * the statement handle.
147     *
148     * @return string error code.
149     */
150    public function errorCode()
151    {
152        if (!$this->_stmt) {
153            return false;
154        }
155        return substr($this->_stmt->sqlstate, 0, 5);
156    }
157
158    /**
159     * Retrieves an array of error information, if any, associated with the
160     * last operation on the statement handle.
161     *
162     * @return array
163     */
164    public function errorInfo()
165    {
166        if (!$this->_stmt) {
167            return false;
168        }
169        return array(
170            substr($this->_stmt->sqlstate, 0, 5),
171            $this->_stmt->errno,
172            $this->_stmt->error,
173        );
174    }
175
176    /**
177     * Executes a prepared statement.
178     *
179     * @param array $params OPTIONAL Values to bind to parameter placeholders.
180     * @return bool
181     * @throws Zend_Db_Statement_Mysqli_Exception
182     */
183    public function _execute(array $params = null)
184    {
185        if (!$this->_stmt) {
186            return false;
187        }
188
189        // if no params were given as an argument to execute(),
190        // then default to the _bindParam array
191        if ($params === null) {
192            $params = $this->_bindParam;
193        }
194        // send $params as input parameters to the statement
195        if ($params) {
196            array_unshift($params, str_repeat('s', count($params)));
197            $stmtParams = array();
198            foreach ($params as $k => &$value) {
199                $stmtParams[$k] = &$value;
200            }
201            call_user_func_array(
202                array($this->_stmt, 'bind_param'),
203                $stmtParams
204                );
205        }
206
207        // execute the statement
208        $retval = $this->_stmt->execute();
209        if ($retval === false) {
210            /**
211             * @see Zend_Db_Statement_Mysqli_Exception
212             */
213            // require_once 'Zend/Db/Statement/Mysqli/Exception.php';
214            throw new Zend_Db_Statement_Mysqli_Exception("Mysqli statement execute error : " . $this->_stmt->error, $this->_stmt->errno);
215        }
216
217
218        // retain metadata
219        if ($this->_meta === null) {
220            $this->_meta = $this->_stmt->result_metadata();
221            if ($this->_stmt->errno) {
222                /**
223                 * @see Zend_Db_Statement_Mysqli_Exception
224                 */
225                // require_once 'Zend/Db/Statement/Mysqli/Exception.php';
226                throw new Zend_Db_Statement_Mysqli_Exception("Mysqli statement metadata error: " . $this->_stmt->error, $this->_stmt->errno);
227            }
228        }
229
230        // statements that have no result set do not return metadata
231        if ($this->_meta !== false) {
232
233            // get the column names that will result
234            $this->_keys = array();
235            foreach ($this->_meta->fetch_fields() as $col) {
236                $this->_keys[] = $this->_adapter->foldCase($col->name);
237            }
238
239            // set up a binding space for result variables
240            $this->_values = array_fill(0, count($this->_keys), null);
241
242            // set up references to the result binding space.
243            // just passing $this->_values in the call_user_func_array()
244            // below won't work, you need references.
245            $refs = array();
246            foreach ($this->_values as $i => &$f) {
247                $refs[$i] = &$f;
248            }
249
250            $this->_stmt->store_result();
251            // bind to the result variables
252            call_user_func_array(
253                array($this->_stmt, 'bind_result'),
254                $this->_values
255            );
256        }
257        return $retval;
258    }
259
260
261    /**
262     * Fetches a row from the result set.
263     *
264     * @param int $style  OPTIONAL Fetch mode for this fetch operation.
265     * @param int $cursor OPTIONAL Absolute, relative, or other.
266     * @param int $offset OPTIONAL Number for absolute or relative cursors.
267     * @return mixed Array, object, or scalar depending on fetch mode.
268     * @throws Zend_Db_Statement_Mysqli_Exception
269     */
270    public function fetch($style = null, $cursor = null, $offset = null)
271    {
272        if (!$this->_stmt) {
273            return false;
274        }
275        // fetch the next result
276        $retval = $this->_stmt->fetch();
277        switch ($retval) {
278            case null: // end of data
279            case false: // error occurred
280                $this->_stmt->reset();
281                return false;
282            default:
283                // fallthrough
284        }
285
286        // make sure we have a fetch mode
287        if ($style === null) {
288            $style = $this->_fetchMode;
289        }
290
291        // dereference the result values, otherwise things like fetchAll()
292        // return the same values for every entry (because of the reference).
293        $values = array();
294        foreach ($this->_values as $key => $val) {
295            $values[] = $val;
296        }
297
298        $row = false;
299        switch ($style) {
300            case Zend_Db::FETCH_NUM:
301                $row = $values;
302                break;
303            case Zend_Db::FETCH_ASSOC:
304                $row = array_combine($this->_keys, $values);
305                break;
306            case Zend_Db::FETCH_BOTH:
307                $assoc = array_combine($this->_keys, $values);
308                $row = array_merge($values, $assoc);
309                break;
310            case Zend_Db::FETCH_OBJ:
311                $row = (object) array_combine($this->_keys, $values);
312                break;
313            case Zend_Db::FETCH_BOUND:
314                $assoc = array_combine($this->_keys, $values);
315                $row = array_merge($values, $assoc);
316                return $this->_fetchBound($row);
317                break;
318            default:
319                /**
320                 * @see Zend_Db_Statement_Mysqli_Exception
321                 */
322                // require_once 'Zend/Db/Statement/Mysqli/Exception.php';
323                throw new Zend_Db_Statement_Mysqli_Exception("Invalid fetch mode '$style' specified");
324                break;
325        }
326        return $row;
327    }
328
329    /**
330     * Retrieves the next rowset (result set) for a SQL statement that has
331     * multiple result sets.  An example is a stored procedure that returns
332     * the results of multiple queries.
333     *
334     * @return bool
335     * @throws Zend_Db_Statement_Mysqli_Exception
336     */
337    public function nextRowset()
338    {
339        /**
340         * @see Zend_Db_Statement_Mysqli_Exception
341         */
342        // require_once 'Zend/Db/Statement/Mysqli/Exception.php';
343        throw new Zend_Db_Statement_Mysqli_Exception(__FUNCTION__.'() is not implemented');
344    }
345
346    /**
347     * Returns the number of rows affected by the execution of the
348     * last INSERT, DELETE, or UPDATE statement executed by this
349     * statement object.
350     *
351     * @return int     The number of rows affected.
352     */
353    public function rowCount()
354    {
355        if (!$this->_adapter) {
356            return false;
357        }
358        $mysqli = $this->_adapter->getConnection();
359        return $mysqli->affected_rows;
360    }
361
362}
363