1<?php
2/**
3 * This file was originally written by Chris Petersen for several different open
4 * source projects.  It is distrubuted under the GNU General Public License.
5 * I (Chris Petersen) have also granted a special LGPL license for this code to
6 * several companies I do work for on the condition that these companies will
7 * release any changes to this back to me and the open source community as GPL,
8 * thus continuing to improve the open source version of the library.  If you
9 * would like to inquire about the status of this arrangement, please contact
10 * me personally.
11 *
12 * ---
13 *
14 * Query handler for the "compatible" version of the improved MySQL engine.
15 * This is also the parent class for the full/expanded mysqli query object,
16 * since many of the routines are shared between them.
17 *
18 * @copyright   Silicon Mechanics
19 * @license     GPL
20 *
21 * @package     MythWeb
22 * @subpackage  Database
23 *
24 * @uses        Database.php
25 * @uses        Database_mysqlicompat.php
26 * @uses        Database_Query.php
27 *
28 **/
29
30/**
31 * The basic mysqli database query type.
32 **/
33class Database_Query_mysqlicompat extends Database_Query {
34
35/**
36 * Executes the query that was previously passed to the constructor.
37 *
38 * @param mixed  $arg      Query arguments to escape and insert at ? placeholders in $query
39 * @param mixed  ...       Additional arguments
40 **/
41    function execute() {
42    // Load the function arguments, minus the query itself, which we already extracted
43        $args = func_get_args();
44    // Split out sub-arrays, etc..
45        $args = Database::smart_args($args);
46    // Were enough arguments passed in?
47        if (count($args) != $this->num_args_needed)
48            trigger_error('Database_Query_mysqlicompat called with '.count($args)." arguments, but requires $this->num_args_needed.", E_USER_ERROR);
49    // Finish any previous statements
50        $this->finish();
51    // Replace in the arguments
52        $this->last_query = '';
53        foreach ($this->query as $part) {
54            $this->last_query .= $part;
55            if (count($args)) {
56                $arg = array_shift($args);
57                $this->last_query .= is_null($arg)
58                                        ? 'NULL'
59                                        : "'".mysqli_real_escape_string($this->dbh, $arg)."'";
60            }
61        }
62    // Perform the query
63    // If we don't have a valid connection, fataly error out.
64        if ($this->dbh === false) {
65            $this->db->error();
66            trigger_error($this->db->error, E_USER_ERROR);
67        }
68        $this->sh = mysqli_query($this->dbh, $this->last_query);
69
70    // Cache  these so the warning count below doesn't interfere
71        if (is_bool($this->sh)) {
72            $this->insert_id     = mysqli_insert_id($this->dbh);
73            $this->affected_rows = mysqli_affected_rows($this->dbh);
74        }
75        else {
76            $this->num_rows      = mysqli_num_rows($this->sh);
77        }
78
79    // On each execute, we clear the warnings of the statement handle, so it doesn't
80    // store them up
81        $this->warnings = array();
82    // Check the warnings and store them
83        if (mysqli_warning_count($this->dbh)) {
84            if ($sh = mysqli_query($this->dbh, 'SHOW WARNINGS')) {
85                while ($row = mysqli_fetch_row($sh))
86                    $this->warnings[] = array( '#'   => $row[1],
87                                               'MSG' => $row[2] );
88                mysqli_free_result($sh);
89            // This is used in errors.php to output in the backtrace
90                global $_DEBUG;
91                $_DEBUG['Database Warnings'][] = array( 'Query'    => $this->last_query,
92                                                        'Warnings' => $this->warnings );
93            }
94        }
95
96        if ($this->sh === false) {
97            if ($this->db->fatal_errors)
98                trigger_error('SQL Error: '.mysqli_error($this->dbh).' [#'.mysqli_errno($this->dbh).']', E_USER_ERROR);
99            else
100                $this->db->error();
101        }
102    }
103
104/**
105 * The following routines basically replicate the mysqli functions built into
106 * php.  The only difference is that the resource handle gets passed-in
107 * automatically.  eg.
108 *
109 *      mysqli_fetch_row($result);   ->  $sh->fetch_row();
110 *      mysqli_affected_rows($dbh);  ->  $sh->affected_rows();
111 **/
112
113/**
114 * Fetch a single column
115 * @return mixed
116 **/
117    function fetch_col() {
118        list($return) = mysqli_fetch_row($this->sh);
119        return $return;
120    }
121
122    function fetch_cols() {
123        $return = array();
124        while ($col = $this->fetch_col())
125            $return[] = $col;
126        return $return;
127    }
128
129/**
130 * Fetch a single row
131 *
132 * @link http://www.php.net/manual/en/function.mysqli-fetch-row.php
133 * @return array
134 **/
135    function fetch_row() {
136        return mysqli_fetch_row($this->sh);
137    }
138
139/**
140 * Fetch a single assoc row
141 *
142 * @link http://www.php.net/manual/en/function.mysqli-fetch-assoc.php
143 * @return assoc
144 **/
145    function fetch_assoc() {
146        return mysqli_fetch_assoc($this->sh);
147    }
148
149/**
150 * Fetch a single row as an array containing both numeric and assoc fields
151 *
152 * @link http://www.php.net/manual/en/function.mysqli-fetch-array.php
153 * @return assoc
154 **/
155    function fetch_array($result_type=MYSQLI_BOTH) {
156        return mysqli_fetch_array($this->sh, $result_type);
157    }
158
159/**
160 * Fetch a single row as an object
161 *
162 * @link http://www.php.net/manual/en/function.mysqli-fetch-object.php
163 * @return object
164 **/
165    function fetch_object() {
166        return mysqli_fetch_object($this->sh);
167    }
168
169/**
170 * @link http://www.php.net/manual/en/function.mysqli-data-seek.php
171 * @return bool
172 **/
173    function data_seek($row_number) {
174        return mysqli_data_seek($this->sh, $row_number);
175    }
176
177/**
178 * @link http://www.php.net/manual/en/function.mysqli-num-rows.php
179 * @return int
180 **/
181    function num_rows() {
182        return $this->num_rows;
183    }
184
185/**
186 * @link http://www.php.net/manual/en/function.mysqli-data-seek.php
187 * @return int
188 **/
189    function affected_rows() {
190        return $this->affected_rows;
191    }
192
193/**
194 * @link http://www.php.net/manual/en/function.mysqli-insert-id.php
195 * @return int
196 **/
197    function insert_id() {
198        return $this->insert_id;
199    }
200
201/**
202 * For anal people like me who like to free up memory manually
203 **/
204    function finish() {
205        if ($this->sh && is_resource($this->sh))
206            mysqli_free_result($this->sh);
207        unset($this->sh);
208    }
209
210}
211