1<?php
2/**
3 * Copyright 2013-2017 Horde LLC (http://www.horde.org/)
4 *
5 * @author     Jan Schneider <jan@horde.org>
6 * @license    http://www.horde.org/licenses/bsd
7 * @category   Horde
8 * @package    Db
9 * @subpackage Adapter
10 */
11
12/**
13 * This class represents the result set of a SELECT query.
14 *
15 * @author     Jan Schneider <jan@horde.org>
16 * @license    http://www.horde.org/licenses/bsd
17 * @category   Horde
18 * @package    Db
19 * @subpackage Adapter
20 */
21abstract class Horde_Db_Adapter_Base_Result implements Iterator
22{
23    /**
24     * @var Horde_Db_Adapter
25     */
26    protected $_adapter;
27
28    /**
29     * @var string
30     */
31    protected $_sql;
32
33    /**
34     * @var mixed
35     */
36    protected $_arg1;
37
38    /**
39     * @var string
40     */
41    protected $_arg2;
42
43    /**
44     * Result resource.
45     *
46     * @var resource
47     */
48    protected $_result;
49
50    /**
51     * Current row.
52     *
53     * @var array
54     */
55    protected $_current;
56
57    /**
58     * Current offset.
59     *
60     * @var integer
61     */
62    protected $_index;
63
64    /**
65     * Are we at the end of the result?
66     *
67     * @var boolean
68     */
69    protected $_eof;
70
71    /**
72     * Which kind of keys to use for results.
73     */
74    protected $_fetchMode = Horde_Db::FETCH_ASSOC;
75
76    /**
77     * Constructor.
78     *
79     * @param Horde_Db_Adapter $adapter  A driver instance.
80     * @param string $sql                A SQL query.
81     * @param mixed $arg1                Either an array of bound parameters or
82     *                                   a query name.
83     * @param string $arg2               If $arg1 contains bound parameters,
84     *                                   the query name.
85     */
86    public function __construct($adapter, $sql, $arg1 = null, $arg2 = null)
87    {
88        $this->_adapter = $adapter;
89        $this->_sql = $sql;
90        $this->_arg1 = $arg1;
91        $this->_arg2 = $arg2;
92    }
93
94    /**
95     * Destructor.
96     */
97    public function __destruct()
98    {
99        if ($this->_result) {
100            unset($this->_result);
101        }
102    }
103
104    /**
105     * Implementation of the rewind() method for iterator.
106     */
107    public function rewind()
108    {
109        if ($this->_result) {
110            unset($this->_result);
111        }
112        $this->_current = null;
113        $this->_index = null;
114        $this->_eof = true;
115        $this->_result = $this->_adapter->execute(
116            $this->_sql, $this->_arg1, $this->_arg2
117        );
118
119        $this->next();
120    }
121
122    /**
123     * Implementation of the current() method for Iterator.
124     *
125     * @return array  The current row, or null if no rows.
126     */
127    public function current()
128    {
129        if (is_null($this->_result)) {
130            $this->rewind();
131        }
132        return $this->_current;
133    }
134
135    /**
136     * Implementation of the key() method for Iterator.
137     *
138     * @return mixed  The current row number (starts at 0), or null if no rows.
139     */
140    public function key()
141    {
142        if (is_null($this->_result)) {
143            $this->rewind();
144        }
145        return $this->_index;
146    }
147
148    /**
149     * Implementation of the next() method for Iterator.
150     *
151     * @return array|null  The next row in the resultset or null if there are
152     *                     no more results.
153     */
154    public function next()
155    {
156        if (is_null($this->_result)) {
157            $this->rewind();
158        }
159
160        if ($this->_result) {
161            $row = $this->_fetchArray();
162            if (!$row) {
163                $this->_eof = true;
164            } else {
165                $this->_eof = false;
166
167                if (is_null($this->_index)) {
168                    $this->_index = 0;
169                } else {
170                    ++$this->_index;
171                }
172
173                $this->_current = $row;
174            }
175        }
176
177        return $this->_current;
178    }
179
180    /**
181     * Implementation of the valid() method for Iterator.
182     *
183     * @return boolean  Whether the iteration is valid.
184     */
185    public function valid()
186    {
187        if (is_null($this->_result)) {
188            $this->rewind();
189        }
190        return !$this->_eof;
191    }
192
193    /**
194     * Returns the current row and advances the recordset one row.
195     *
196     * @param integer $fetchmode  The default fetch mode for this result. One
197     *                            of the Horde_Db::FETCH_* constants.
198     */
199    public function fetch($fetchmode = Horde_Db::FETCH_ASSOC)
200    {
201        if (!$this->valid()) {
202            return null;
203        }
204        $this->setFetchMode($fetchmode);
205        $row = $this->current();
206        $this->next();
207        return $row;
208    }
209
210    /**
211     * Sets the default fetch mode for this result.
212     *
213     * @param integer $fetchmode  One of the Horde_Db::FETCH_* constants.
214     */
215    public function setFetchMode($fetchmode)
216    {
217        $this->_fetchMode = $fetchmode;
218    }
219
220    /**
221     * Returns the number of columns in the result set.
222     *
223     * @return integer  Number of columns.
224     */
225    public function columnCount()
226    {
227        if (is_null($this->_result)) {
228            $this->rewind();
229        }
230        return $this->_columnCount();
231    }
232
233    /**
234     * Returns a row from a resultset.
235     *
236     * @return array|boolean  The next row in the resultset or false if there
237     *                        are no more results.
238     */
239    abstract protected function _fetchArray();
240
241    /**
242     * Returns the number of columns in the result set.
243     *
244     * @return integer  Number of columns.
245     */
246    abstract protected function _columnCount();
247}
248