prefixing require_once statements- previous version worked only for for a few minutes...
[lhc/web/wiklou.git] / PHPTAL-NP-0.7.0 / libs / PHPTAL / LoopControler.php
1 <?php
2 /* vim: set expandtab tabstop=4 shiftwidth=4: */
3 //
4 // Copyright (c) 2003 Laurent Bedubourg
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 //
20 // Authors: Laurent Bedubourg <laurent.bedubourg@free.fr>
21 //
22
23 require_once PT_IP . "/Types/OArray.php";
24 require_once PT_IP . "/Types/OHash.php";
25 require_once PT_IP . "/Types/Iterator.php";
26
27 /**
28 * Template loop execution controler.
29 *
30 * This object is instantiated by the template on each loop.
31 *
32 * LoopControlers accept different types of loop target.
33 *
34 * - array
35 * - objects having an getNewIterator() method returning an Iterator object.
36 * - Iterator objects which produce isValid(), next(), key() and index()
37 * methods.
38 * please note that key() return index() for non associative data.
39 *
40 * Other types are rejected by the loop controler on runtime producing
41 * a TypeError exception.
42 *
43 * The loop controler install its iterator under the path "repeat/item"
44 * where "item" is the name of the output var (defined in the template).
45 *
46 * Thus, the template can access differents methods of the iterator:
47 * repeat/someitem/key
48 * repeat/someitem/index
49 * repeat/someitem/next
50 * repeat/someitem/last
51 *
52 * If not all of these methods are implemented in iterators, ask
53 * iterator maintainers.
54 *
55 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
56 *
57 */
58 class PHPTAL_LoopControler
59 {
60 var $_context;
61 var $_data;
62 var $_data_name;
63 var $_iterator;
64 var $_error;
65
66 /**
67 * Controler constructor.
68 *
69 * @param PHPTAL_Context $context
70 * The template context.
71 * @param string $data_name
72 * The item data name.
73 * @param mixed $data
74 * Loop resource.
75 */
76 function PHPTAL_LoopControler(&$context, $data_name, $data)
77 {
78 $this->_context =& $context;
79 $this->_data =& $data;
80 $this->_data_name = $data_name;
81
82 // ensure that data is not an error
83 if (PEAR::isError($data)) {
84 $this->_error =& $data;
85 return $data;
86 }
87
88 // accepted objects
89 //
90 // - iteratable implementing getNewIterator() method
91 // - iterator implementing next(), isValid() and index() methods
92 // - db_result produced by PEAR::DB package
93 //
94 if (is_object($data)) {
95 if (method_exists($data, "getNewIterator")) {
96
97 $this->_iterator =& $data->getNewIterator();
98
99 } elseif (is_a("iterator", $data)
100 || (method_exists($data, 'next')
101 && method_exists($data, 'isValid')
102 && method_exists($data, 'index'))) {
103
104 $this->_iterator =& $data;
105
106 } elseif (get_class($data) == 'db_result') {
107
108 $this->_iterator = new PHPTAL_DBResultIterator($data);
109
110 } else {
111
112 $err = new TypeError("PHPTAL loop controler received a non Iterable object ("
113 . get_class($data) . ")");
114 $this->_error =& $err;
115 return PEAR::raiseError($err);
116 }
117 } elseif (is_array($data)) {
118 //
119 // array are accepted thanks to OArrayIterator
120 //
121 reset($data);
122 if (count($data) > 0 && array_key_exists(0, $data)) {
123 // echo "OArray::iterator", RxObject::ClassName($data), endl;
124 $this->_data = new OArray($data);
125 $this->_iterator =& $this->_data->getNewIterator();
126 } else {
127 // echo "OHash::iterator", RxObject::ClassName($data), endl;
128 $this->_data = new OHash($data);
129 $this->_iterator =& $this->_data->getNewIterator();
130 }
131 } else {
132 $err = new TypeError("phptal loop controler received a non Iterable value ("
133 . gettype($data) . ")");
134 $this->_error =& $err;
135 return PEAR::raiseError($err);
136 }
137
138 //
139 // install loop in repeat context array
140 //
141 $repeat =& $this->_context->get("repeat");
142 if (array_key_exists($this->_data_name, $repeat)) {
143 unset($repeat[$this->_data_name]);
144 }
145 $repeat[$this->_data_name] =& $this;
146
147 // $this->_context->setRef($this->_data_name, $temp);
148 $temp =& $this->_iterator->value();
149 $this->_context->set($this->_data_name, $temp);
150 return $temp;
151 }
152
153 /**
154 * Return current item index.
155 *
156 * @return int
157 */
158 function index()
159 {
160 return $this->_iterator->index();
161 }
162
163 /**
164 * Return current item key or index for non associative iterators.
165 *
166 * @return mixed
167 */
168 function key()
169 {
170 if (method_exists($this->_iterator, "key")) {
171 return $this->_iterator->key();
172 } else {
173 return $this->_iterator->index();
174 }
175 }
176
177 /**
178 * Index is in range(0, length-1), the number in in range(1, length).
179 *
180 * @return int
181 */
182 function number()
183 {
184 return $this->index() + 1;
185 }
186
187 /**
188 * Return true if index is even.
189 *
190 * @return boolean
191 */
192 function even()
193 {
194 return !$this->odd();
195 }
196
197 /**
198 * Return true if index is odd.
199 *
200 * @return boolean
201 */
202 function odd()
203 {
204 return ($this->index() % 2);
205 }
206
207 /**
208 * Return true if at the begining of the loop.
209 *
210 * @return boolean
211 */
212 function start()
213 {
214 return ($this->index() == 0);
215 }
216
217 /**
218 * Return true if at the end of the loop (no more item).
219 *
220 * @return boolean
221 */
222 function end()
223 {
224 return ($this->length() == $this->number());
225 }
226
227 function isValid()
228 {
229 return $this->_iterator->isValid();
230 }
231
232 /**
233 * Return the length of the data (total number of iterations).
234 *
235 * @return int
236 */
237 function length()
238 {
239 return $this->_data->size();
240 }
241
242 /**
243 * Retrieve next iterator value.
244 *
245 * @return mixed
246 */
247 function &next()
248 {
249 $temp =& $this->_iterator->next();
250 if (!$this->_iterator->isValid()) {
251 return false;
252 }
253
254 // $this->_context->setRef($this->_data_name, $temp);
255 $this->_context->set($this->_data_name, $temp);
256 return $temp;
257 }
258 };
259
260
261 /**
262 * Iterator for DB_Result PEAR object.
263 *
264 * This class is an implementation of the Iterator Interface
265 * for DB_Result objects produced by the usage of PEAR::DB package.
266 *
267 * @author Laurent Bedubourg <laurent.bedubourg@free.fr>
268 */
269 class PHPTAL_DBResultIterator extends Iterator
270 {
271 var $_src;
272 var $_index = -1;
273 var $_end = false;
274 var $_value;
275
276 /**
277 * Iterator constructor.
278 *
279 * @param DB_Result $result
280 * The query result.
281 */
282 function PHPTAL_DBResultIterator(&$result)
283 {
284 $this->_src =& $result;
285 $this->reset();
286 }
287
288 function reset()
289 {
290 if ($this->size() == 0) {
291 $this->_end = true;
292 return;
293 }
294
295 $this->_index = 0;
296 $this->_end = false;
297 unset($this->_value);
298 $this->_value = $this->_src->fetchRow();
299 }
300
301 /**
302 * Return the number of rows in this result.
303 *
304 * @return int
305 */
306 function size()
307 {
308 if (!isset($this->_size)) {
309 $this->_size = $this->_src->numRows();
310 }
311 return $this->_size;
312 }
313
314 /**
315 * Returns true if end of iterator has not been reached yet.
316 */
317 function isValid()
318 {
319 return !$this->_end;
320 }
321
322 /**
323 * Return the next row in this result.
324 *
325 * This method calls fetchRow() on the DB_Result, the return type depends
326 * of the DB_result->fetchmod. Please specify it before executing the
327 * template.
328 *
329 * @return mixed
330 */
331 function &next()
332 {
333 if ($this->_end || ++ $this->_index >= $this->size()) {
334 $this->_end = true;
335 return false;
336 }
337
338 unset($this->_value);
339 $this->_value = $this->_src->fetchRow();
340 return $this->_value;
341 }
342
343 /**
344 * Return current row.
345 *
346 * @return mixed
347 */
348 function &value()
349 {
350 return $this->_value;
351 }
352
353 /**
354 * Return current row index in resultset.
355 *
356 * @return int
357 */
358 function index()
359 {
360 return $this->_index;
361 }
362 }
363
364 ?>