Fix SUID.
[platal.git] / classes / plfilter.php
1 <?php
2 /***************************************************************************
3 * Copyright (C) 2003-2010 Polytechnique.org *
4 * http://opensource.polytechnique.org/ *
5 * *
6 * This program is free software; you can redistribute it and/or modify *
7 * it under the terms of the GNU General Public License as published by *
8 * the Free Software Foundation; either version 2 of the License, or *
9 * (at your option) any later version. *
10 * *
11 * This program 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 *
14 * GNU General Public License for more details. *
15 * *
16 * You should have received a copy of the GNU General Public License *
17 * along with this program; if not, write to the Free Software *
18 * Foundation, Inc., *
19 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA *
20 ***************************************************************************/
21
22 __autoload('xdb');
23
24 // {{{ class PlLimit
25 class PlLimit
26 {
27 private $count = null;
28 private $from = null;
29
30 public function __construct($count = null, $from = null)
31 {
32 $this->count = $count;
33 $this->from = $from;
34 }
35
36 public function getSql()
37 {
38 if (!is_null($this->count) && $this->count != 0) {
39 if (!is_null($this->from) && $this->from != 0) {
40 return XDB::format('LIMIT {?}, {?}', (int)$this->from, (int)$this->count);
41 } else {
42 return XDB::format('LIMIT {?}', (int)$this->count);
43 }
44 }
45 return '';
46 }
47 }
48 // }}}
49
50 // {{{ class PlFilterOrder
51 /** Base class for ordering results of a query.
52 * Parameters for the ordering must be given to the constructor ($desc for a
53 * descending order).
54 * The getSortTokens function is used to get actual ordering part of the query.
55 */
56 abstract class PlFilterOrder
57 {
58 protected $desc = false;
59 public function __construct($desc = false)
60 {
61 $this->desc = $desc;
62 $this->_tokens = null;
63 }
64
65 public function toggleDesc()
66 {
67 $this->desc = !$this->desc;
68 }
69
70 public function setDescending($desc = true)
71 {
72 $this->desc = $desc;
73 }
74
75 public function buildSort(PlFilter &$pf)
76 {
77 $sel = $this->getSortTokens($pf);
78 $this->_tokens = $sel;
79 if (!is_array($sel)) {
80 $sel = array($sel);
81 }
82 if ($this->desc) {
83 foreach ($sel as $k => $s) {
84 $sel[$k] = $s . ' DESC';
85 }
86 }
87 return $sel;
88 }
89
90 /** This function must return the tokens to use for ordering
91 * @param &$pf The PlFilter whose results must be ordered
92 * @return The name of the field to use for ordering results
93 */
94 abstract protected function getSortTokens(PlFilter &$pf);
95 }
96 // }}}
97
98 // {{{ class PlFilterGroupableOrder
99 /** Extension of a PlFilterOrder, for orders where the value on which ordering
100 * is done could be used for grouping results (promo, country, ...)
101 */
102 abstract class PlFilterGroupableOrder extends PlFilterOrder
103 {
104 /** This function will be called when trying to retrieve groups;
105 * the returned token will be used to group the values.
106 * It will always be called AFTER getSortTokens().
107 */
108 public function getGroupToken(PlFilter &$pf)
109 {
110 return $this->_tokens;
111 }
112 }
113 // }}}
114
115 // {{{ class PFO_Random
116 class PFO_Random extends PlFilterOrder
117 {
118 private $seed = null;
119
120 public function __construct($seed = null, $desc = false)
121 {
122 parent::__construct($desc);
123 $this->seed = $seed;
124 }
125
126 protected function getSortTokens(PlFilter &$pf)
127 {
128 if ($this->seed == null) {
129 return 'RAND()';
130 } else {
131 return XDB::format('RAND({?})', $this->seed);
132 }
133 }
134 }
135 // }}}
136
137 // {{{ interface PlFilterCondition
138 interface PlFilterCondition
139 {
140 const COND_TRUE = 'TRUE';
141 const COND_FALSE = 'FALSE';
142
143 public function buildCondition(PlFilter &$pf);
144 }
145 // }}}
146
147 // {{{ class PFC_OneChild
148 abstract class PFC_OneChild implements PlFilterCondition
149 {
150 protected $child;
151
152 public function __construct(&$child = null)
153 {
154 if (!is_null($child) && ($child instanceof PlFilterCondition)) {
155 $this->setChild($child);
156 }
157 }
158
159 public function setChild(PlFilterCondition &$cond)
160 {
161 $this->child =& $cond;
162 }
163 }
164 // }}}
165
166 // {{{ class PFC_NChildren
167 abstract class PFC_NChildren implements PlFilterCondition
168 {
169 protected $children = array();
170
171 public function __construct()
172 {
173 $this->addChildren(pl_flatten(func_get_args()));
174 }
175
176 public function addChildren(array $conds)
177 {
178 foreach ($conds as &$cond) {
179 if (!is_null($cond) && ($cond instanceof PlFilterCondition)) {
180 $this->addChild($cond);
181 }
182 }
183 }
184
185 public function addChild(PlFilterCondition &$cond)
186 {
187 $this->children[] =& $cond;
188 }
189
190 protected function catConds(array $cond, $op, $fallback)
191 {
192 if (count($cond) == 0) {
193 return $fallback;
194 } else if (count($cond) == 1) {
195 return $cond[0];
196 } else {
197 return '(' . implode(') ' . $op . ' (', $cond) . ')';
198 }
199 }
200 }
201 // }}}
202
203 // {{{ class PFC_True
204 class PFC_True implements PlFilterCondition
205 {
206 public function buildCondition(PlFilter &$uf)
207 {
208 return self::COND_TRUE;
209 }
210 }
211 // }}}
212
213 // {{{ class PFC_False
214 class PFC_False implements PlFilterCondition
215 {
216 public function buildCondition(PlFilter &$uf)
217 {
218 return self::COND_FALSE;
219 }
220 }
221 // }}}
222
223 // {{{ class PFC_Not
224 class PFC_Not extends PFC_OneChild
225 {
226 public function buildCondition(PlFilter &$uf)
227 {
228 $val = $this->child->buildCondition($uf);
229 if ($val == self::COND_TRUE) {
230 return self::COND_FALSE;
231 } else if ($val == self::COND_FALSE) {
232 return self::COND_TRUE;
233 } else {
234 return 'NOT (' . $val . ')';
235 }
236 }
237 }
238 // }}}
239
240 // {{{ class PFC_And
241 class PFC_And extends PFC_NChildren
242 {
243 public function buildCondition(PlFilter &$uf)
244 {
245 if (empty($this->children)) {
246 return self::COND_FALSE;
247 } else {
248 $true = self::COND_FALSE;
249 $conds = array();
250 foreach ($this->children as &$child) {
251 $val = $child->buildCondition($uf);
252 if ($val == self::COND_TRUE) {
253 $true = self::COND_TRUE;
254 } else if ($val == self::COND_FALSE) {
255 return self::COND_FALSE;
256 } else {
257 $conds[] = $val;
258 }
259 }
260 return $this->catConds($conds, 'AND', $true);
261 }
262 }
263 }
264 // }}}
265
266 // {{{ class PFC_Or
267 class PFC_Or extends PFC_NChildren
268 {
269 public function buildCondition(PlFilter &$uf)
270 {
271 if (empty($this->children)) {
272 return self::COND_TRUE;
273 } else {
274 $true = self::COND_TRUE;
275 $conds = array();
276 foreach ($this->children as &$child) {
277 $val = $child->buildCondition($uf);
278 if ($val == self::COND_TRUE) {
279 return self::COND_TRUE;
280 } else if ($val == self::COND_FALSE) {
281 $true = self::COND_FALSE;
282 } else {
283 $conds[] = $val;
284 }
285 }
286 return $this->catConds($conds, 'OR', $true);
287 }
288 }
289 }
290 // }}}
291
292 // {{{ class PlFilter
293 abstract class PlFilter
294 {
295 /** Filters objects matching the PlFilter
296 * @param $objects The objects to filter
297 * @param $limit The portion of the matching objects to show
298 */
299 public abstract function filter(array $objects, $limit = null);
300
301 public abstract function setCondition(PlFilterCondition &$cond);
302
303 public abstract function addSort(PlFilterOrder &$sort);
304
305 public abstract function getTotalCount();
306
307 /** Whether this PlFilter can return grouped results through
308 * $this->getGroups();
309 */
310 public abstract function hasGroups();
311
312 /** Used to retrieve value/amount resulting from grouping by the first
313 * given order.
314 */
315 public abstract function getGroups();
316
317 /** Get objects, selecting only those within a limit
318 * @param $limit The portion of the matching objects to select
319 */
320 public abstract function get($limit = null);
321
322 /** PRIVATE FUNCTIONS
323 */
324
325 /** List of metas to replace in joins:
326 * '$COIN' => 'pan.x' means 'replace $COIN with pan.x in the condition of the joins'
327 *
328 * "$ME" => "joined table alias" is always added to these.
329 */
330 protected $joinMetas = array();
331
332 protected $joinMethods = array();
333
334 /** Build the 'join' part of the query
335 * This function will call all methods declared in self::$joinMethods
336 * to get an array of PlSqlJoin objects to merge
337 */
338 protected function buildJoins()
339 {
340 $joins = array();
341 foreach ($this->joinMethods as $method) {
342 $joins = array_merge($joins, $this->$method());
343 }
344 return PlSqlJoin::formatJoins($joins, $this->joinMetas);
345 }
346
347 }
348 // }}}
349
350 // vim:set et sw=4 sts=4 sws=4 foldmethod=marker enc=utf-8:
351 ?>