Passed
Push — master ( a58f5f...4784f2 )
by Michael
14:09 queued 06:48
created

Criteria::render()   C

Complexity

Conditions 12
Paths 96

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
eloc 17
c 2
b 0
f 0
nc 96
nop 0
dl 0
loc 27
rs 6.9666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * XOOPS Criteria parser for database query
4
 *
5
 * You may not change or alter any portion of this comment or credits
6
 * of supporting developers from this source code or any supporting source code
7
 * which is considered copyrighted (c) material of the original comment or credit authors.
8
 * This program is distributed in the hope that it will be useful,
9
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11
 *
12
 * @copyright       (c) 2000-2016 XOOPS Project (www.xoops.org)
13
 * @license             GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14
 * @package             kernel
15
 * @subpackage          database
16
 * @since               2.0.0
17
 * @author              Kazumi Ono <[email protected]>
18
 * @author              Nathan Dial
19
 * @author              Taiwen Jiang <[email protected]>
20
 */
21
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
22
23
/**
24
 * A criteria (grammar?) for a database query.
25
 *
26
 * Abstract base class should never be instantiated directly.
27
 *
28
 * @abstract
29
 */
30
class CriteriaElement
31
{
32
    /**
33
     * Sort order
34
     *
35
     * @var string
36
     */
37
    public $order = 'ASC';
38
39
    /**
40
     *
41
     * @var string
42
     */
43
    public $sort = '';
44
45
    /**
46
     * Number of records to retrieve
47
     *
48
     * @var int
49
     */
50
    public $limit = 0;
51
52
    /**
53
     * Offset of first record
54
     *
55
     * @var int
56
     */
57
    public $start = 0;
58
59
    /**
60
     *
61
     * @var string
62
     */
63
    public $groupby = '';
64
65
    /**
66
     * Constructor
67
     */
68
    public function __construct() {}
69
70
    /**
71
     * Render the criteria element
72
     * @return string
73
     */
74
    public function render() {}
75
76
    /**
77
     *
78
     * @param string $sort
79
     */
80
    public function setSort($sort)
81
    {
82
        $this->sort = $sort;
83
    }
84
85
    /**
86
     *
87
     * @return string
88
     */
89
    public function getSort()
90
    {
91
        return $this->sort;
92
    }
93
94
    /**
95
     *
96
     * @param string $order
97
     */
98
    public function setOrder($order)
99
    {
100
        if ('DESC' === strtoupper($order)) {
101
            $this->order = 'DESC';
102
        }
103
    }
104
105
    /**
106
     *
107
     * @return string
108
     */
109
    public function getOrder()
110
    {
111
        return $this->order;
112
    }
113
114
    /**
115
     *
116
     * @param int $limit
117
     */
118
    public function setLimit($limit = 0)
119
    {
120
        $this->limit = (int) $limit;
121
    }
122
123
    /**
124
     *
125
     * @return int
126
     */
127
    public function getLimit()
128
    {
129
        return $this->limit;
130
    }
131
132
    /**
133
     *
134
     * @param int $start
135
     */
136
    public function setStart($start = 0)
137
    {
138
        $this->start = (int) $start;
139
    }
140
141
    /**
142
     *
143
     * @return int
144
     */
145
    public function getStart()
146
    {
147
        return $this->start;
148
    }
149
150
    /**
151
     *
152
     * @param string $group
153
     */
154
    public function setGroupBy($group)
155
    {
156
        $this->groupby = $group;
157
    }
158
159
    /**
160
     *
161
     * @return string
162
     */
163
    public function getGroupby()
164
    {
165
        return $this->groupby ? " GROUP BY {$this->groupby}" : '';
166
    }
167
    /**
168
     * *#@-
169
     */
170
}
171
172
/**
173
 * Collection of multiple {@link CriteriaElement}s
174
 *
175
 */
176
class CriteriaCompo extends CriteriaElement
177
{
178
    /**
179
     * The elements of the collection
180
     *
181
     * @var array Array of {@link CriteriaElement} objects
182
     */
183
    public $criteriaElements = [];
184
185
    /**
186
     * Conditions
187
     *
188
     * @var array
189
     */
190
    public $conditions = [];
191
192
    /**
193
     * Constructor
194
     *
195
     * @param CriteriaElement|null $ele
196
     * @param string $condition
197
     */
198
    public function __construct(?CriteriaElement $ele = null, $condition = 'AND')
199
    {
200
        if (isset($ele)) {
201
            $this->add($ele, $condition);
202
        }
203
    }
204
205
    /**
206
     * Add an element
207
     *
208
     * @param CriteriaElement|object $criteriaElement
209
     * @param string                 $condition
210
     * @return object reference to this collection
211
     */
212
    public function &add(CriteriaElement $criteriaElement, $condition = 'AND')
213
    {
214
        if (is_object($criteriaElement)) {
215
            $this->criteriaElements[] = & $criteriaElement;
216
            $this->conditions[]       = $condition;
217
        }
218
219
        return $this;
220
    }
221
222
    /**
223
     * Make the criteria into a query string
224
     *
225
     * @return string
226
     */
227
    public function render()
228
    {
229
        $ret   = '';
230
        $count = count($this->criteriaElements);
231
        if ($count > 0) {
232
            $render_string = $this->criteriaElements[0]->render();
233
            for ($i = 1; $i < $count; ++$i) {
234
                if (!$render = $this->criteriaElements[$i]->render()) {
235
                    continue;
236
                }
237
                $render_string .= (empty($render_string) ? '' : ' ' . $this->conditions[$i] . ' ') . $render;
238
            }
239
            $ret = empty($render_string) ? '' : "({$render_string})";
240
        }
241
242
        return $ret;
243
    }
244
245
    /**
246
     * Make the criteria into a SQL "WHERE" clause
247
     *
248
     * @return string
249
     */
250
    public function renderWhere()
251
    {
252
        $ret = $this->render();
253
        $ret = ($ret != '') ? 'WHERE ' . $ret : $ret;
254
255
        return $ret;
256
    }
257
258
    /**
259
     * Generate an LDAP filter from criteria
260
     *
261
     * @return string
262
     * @author Nathan Dial [email protected]
263
     */
264
    public function renderLdap()
265
    {
266
        $retval = '';
267
        $count  = count($this->criteriaElements);
268
        if ($count > 0) {
269
            $retval = $this->criteriaElements[0]->renderLdap();
270
            for ($i = 1; $i < $count; ++$i) {
271
                $cond   = strtoupper($this->conditions[$i]);
272
                $op     = ($cond === 'OR') ? '|' : '&';
273
                $retval = "({$op}{$retval}" . $this->criteriaElements[$i]->renderLdap() . ')';
274
            }
275
        }
276
277
        return $retval;
278
    }
279
}
280
281
/**
282
 * A single criteria
283
 *
284
 */
285
class Criteria extends CriteriaElement
286
{
287
    /**
288
     *
289
     * @var string
290
     */
291
    public $prefix;
292
    public $function;
293
    public $column;
294
    public $operator;
295
    public $value;
296
    protected $allowEmptyValue = false;
297
298
    /**
299
     * Constructor
300
     *
301
     * @param string $column
302
     * @param string $value
303
     * @param string $operator
304
     * @param string $prefix
305
     * @param string $function
306
     * @param boolean $allowEmptyValue Flag to allow empty string values in criteria, defaults to false.
307
     */
308
    public function __construct($column, $value = '', $operator = '=', $prefix = '', $function = '', $allowEmptyValue = false)
309
    {
310
        $this->prefix   = $prefix;
311
        $this->function = $function;
312
        $this->column   = $column;
313
        $this->value    = $value;
314
        $this->operator = $operator;
315
        $this->allowEmptyValue = $allowEmptyValue;
316
317
        /**
318
         * A common custom in some older programs was to use "new Criteria(1, '1')" to
319
         * create an always true clause, WHERE 1 = "1"
320
         *
321
         * This is no longer needed and now no longer works. Instead, use "new Criteria('')"
322
         * or "new CriteriaCompo()", either of which will produce no WHERE clause.
323
         *
324
         * The following is a temporary workaround for the old technique
325
         */
326
        if ((int) $column === 1 && (int) $value === 1 && $operator === '=') {
327
            $this->column = '';
328
            $this->value  = '';
329
        }
330
    }
331
332
    /**
333
     * Make a sql condition string
334
     *
335
     * @return string
336
     */
337
    public function render()
338
    {
339
        $backtick = (false === strpos($this->column, '.')) ? '`' : '';
340
        $backtick = (false !== strpos($this->column, '(')) ? '' : $backtick;
341
        $clause = (!empty($this->prefix) ? "{$this->prefix}." : '') . $backtick . $this->column . $backtick;
342
343
        if (!empty($this->function)) {
344
            $clause = sprintf($this->function, $clause);
345
        }
346
347
        if (in_array(strtoupper($this->operator), ['IS NULL', 'IS NOT NULL'])) {
348
            $clause .= ' ' . $this->operator;
349
        } else {
350
            if ('' === ($value = trim((string)$this->value)) && !$this->allowEmptyValue) {
351
                return '';
352
            }
353
            if (!in_array(strtoupper($this->operator), ['IN', 'NOT IN'])) {
354
                if ((substr($value, 0, 1) !== '`') && (substr($value, -1) !== '`')) {
355
                    $value = "'{$value}'";
356
                } elseif (!preg_match('/^[a-zA-Z0-9_\.\-`]*$/', $value)) {
357
                    $value = '``';
358
            }
359
        }
360
            $clause .= " {$this->operator} {$value}";
361
        }
362
363
        return $clause;
364
    }
365
366
    /**
367
     * Generate an LDAP filter from criteria
368
     *
369
     * @return string
370
     * @author Nathan Dial [email protected], improved by Pierre-Eric MENUET [email protected]
371
     */
372
    public function renderLdap()
373
    {
374
        if ($this->operator === '>') {
375
            $this->operator = '>=';
376
        }
377
        if ($this->operator === '<') {
378
            $this->operator = '<=';
379
        }
380
381
        if ($this->operator === '!=' || $this->operator === '<>') {
382
            $operator = '=';
383
            $clause   = '(!(' . $this->column . $operator . $this->value . '))';
384
        } else {
385
            if ($this->operator === 'IN') {
386
                $newvalue = str_replace(['(', ')'], '', $this->value);
387
                $tab      = explode(',', $newvalue);
388
                $clause = '';
389
                foreach ($tab as $uid) {
390
                    $clause .= "({$this->column}={$uid})";
391
                }
392
                $clause = '(|' . $clause . ')';
393
            } else {
394
                $clause = '(' . $this->column . $this->operator . $this->value . ')';
395
            }
396
        }
397
398
        return $clause;
399
    }
400
401
    /**
402
     * Make a SQL "WHERE" clause
403
     *
404
     * @return string
405
     */
406
    public function renderWhere()
407
    {
408
        $cond = $this->render();
409
410
        return empty($cond) ? '' : "WHERE {$cond}";
411
    }
412
}
413