Passed
Push — master ( 1f10fe...ebb318 )
by y
01:23
created

SQL::max()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 2
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Helix\DB;
4
5
use Countable;
6
7
/**
8
 * Static helper for building SQL.
9
 *
10
 * The methods here are driver agnostic, and do not quote values.
11
 */
12
class SQL {
13
14
    /**
15
     * `(... AND ...)`
16
     *
17
     * @param string[] $conditions
18
     * @return string
19
     */
20
    public static function all (array $conditions): string {
21
        if (count($conditions) === 1) {
22
            return reset($conditions);
23
        }
24
        return '(' . implode(' AND ', $conditions) . ')';
25
    }
26
27
    /**
28
     * `(... OR ...)`
29
     *
30
     * @param string[] $conditions
31
     * @return string
32
     */
33
    public static function any (array $conditions): string {
34
        if (count($conditions) === 1) {
35
            return reset($conditions);
36
        }
37
        return '(' . implode(' OR ', $conditions) . ')';
38
    }
39
40
    /**
41
     * Used to generate a comparison.
42
     *
43
     * `"$a $operator $b"`
44
     *
45
     * If `$a` is an array, the keys and values are used for `$a` and `$b`.
46
     *
47
     * `["$k $operator $v", ...]`
48
     *
49
     * If `$b` is an array, all items are listed within parenthesis.
50
     *
51
     * `"$a $listOperator (...$b)"`
52
     *
53
     * As well as if `$a` is a two-dimensional array.
54
     *
55
     * `["$k $listOperator (...$v)", ...]`
56
     *
57
     * If `$b` is a `{@link Select}, it's used as a subquery.
58
     *
59
     * `"$a $operator $subOperator $b->toSql()"`
60
     *
61
     * As well as if `$a` is an array of {@link Select}.
62
     *
63
     * `["$k $operator $subOperator $v->toSql()", ...]`
64
     *
65
     * @param mixed $a
66
     * @param string $operator
67
     * @param mixed $b
68
     * @param string $subOperator
69
     * @param string $listOperator
70
     * @return string|array
71
     */
72
    public static function compare ($a, $operator, $b = null, $subOperator = null, $listOperator = null) {
73
        if (is_array($a)) {
74
            $cmp = [];
75
            foreach ($a as $k => $v) {
76
                $cmp[$k] = static::compare($k, $operator, $v, $subOperator, $listOperator);
77
            }
78
            return $cmp;
79
        }
80
        elseif (is_array($b)) {
81
            return "{$a} {$listOperator} (" . implode(',', $b) . ")";
82
        }
83
        elseif ($b instanceof Select) {
84
            return "{$a} {$operator} {$subOperator} ({$b->toSql()})";
85
        }
86
        return "{$a} {$operator} {$b}";
87
    }
88
89
    /**
90
     * Returns a `GROUP BY` clause listing all given columns,
91
     * or an empty string if there are no columns.
92
     *
93
     * @param array $columns
94
     * @return string
95
     */
96
    public static function group (array $columns): string {
97
        if (!empty($columns)) {
98
            return ' GROUP BY ' . implode(',', $columns);
99
        }
100
        return '';
101
    }
102
103
    /**
104
     * Returns a `HAVING` clause for all conditions,
105
     * or an empty string if there are no conditions.
106
     *
107
     * @param array $conditions
108
     * @return string
109
     */
110
    public static function having (array $conditions): string {
111
        if (!empty($conditions)) {
112
            return ' HAVING ' . static::all($conditions);
113
        }
114
        return '';
115
    }
116
117
    /**
118
     * `$a = $b`, or `$a IN (...$b)`, or `$a = ANY ($b)`
119
     *
120
     * @param string|array $a
121
     * @param string|array|Select $b
122
     * @return string|array
123
     */
124
    public static function isEqual ($a, $b = null) {
125
        return static::compare($a, '=', $b, 'ANY', 'IN');
126
    }
127
128
    /**
129
     * `$a > $b`, or `$a > ALL ($b)`
130
     *
131
     * @param string|array $a
132
     * @param string|Select $b
133
     * @return string|array
134
     */
135
    public static function isGreater ($a, $b = null) {
136
        return static::compare($a, '>', $b, 'ALL');
137
    }
138
139
    /**
140
     * `$a >= $b`, or `$a >= ALL ($b)`
141
     *
142
     * @param string|array $a
143
     * @param string|Select $b
144
     * @return string|array
145
     */
146
    public static function isGreaterOrEqual ($a, $b = null) {
147
        return static::compare($a, '>=', $b, 'ALL');
148
    }
149
150
    /**
151
     * `$a < $b`, or `$a < ALL ($b)`
152
     *
153
     * @param string|array $a
154
     * @param string|Select $b
155
     * @return string|array
156
     */
157
    public static function isLess ($a, $b = null) {
158
        return static::compare($a, '<', $b, 'ALL');
159
    }
160
161
    /**
162
     * `$a <= $b`, or `$a <= ALL ($b)`
163
     *
164
     * @param string|array $a
165
     * @param string|Select $b
166
     * @return string|array
167
     */
168
    public static function isLessOrEqual ($a, $b = null) {
169
        return static::compare($a, '<=', $b, 'ALL');
170
    }
171
172
    /**
173
     * `$x LIKE $pattern`
174
     *
175
     * @param string|array $x
176
     * @param string $pattern
177
     * @return string|array
178
     */
179
    public static function isLike ($x, string $pattern = null) {
180
        return static::compare($x, 'LIKE', $pattern);
181
    }
182
183
    /**
184
     * `$a IS NOT UNKNOWN|TRUE|FALSE`
185
     *
186
     * @param string|array $x
187
     * @param null|bool $identity
188
     * @return string|array
189
     */
190
    public static function isNot ($x, $identity) {
191
        return static::compare($x, 'IS NOT', ['' => 'UNKNOWN', 1 => 'TRUE', 0 => 'FALSE'][$identity]);
192
    }
193
194
    /**
195
     * `$a <> $b`, or `$a NOT IN (...$b)`, or `$a <> ALL ($b)`
196
     *
197
     * @param string|array $a
198
     * @param string|array|Select $b
199
     * @return string|array
200
     */
201
    public static function isNotEqual ($a, $b = null) {
202
        return static::compare($a, '<>', $b, 'ALL', 'NOT IN');
203
    }
204
205
    /**
206
     * `$a NOT LIKE $pattern`
207
     *
208
     * @param string|array $x
209
     * @param string $pattern
210
     * @return string|array
211
     */
212
    public static function isNotLike ($x, string $pattern = null) {
213
        return static::compare($x, 'NOT LIKE', $pattern);
214
    }
215
216
    /**
217
     * `$x IS NOT NULL`
218
     *
219
     * @param string|array $x
220
     * @return string|array
221
     */
222
    public static function isNotNull ($x) {
223
        if (is_array($x)) {
224
            return array_map(__METHOD__, $x);
225
        }
226
        return "{$x} IS NOT NULL";
227
    }
228
229
    /**
230
     * `$x NOT REGEXP $pattern`
231
     *
232
     * @param string|array $x
233
     * @param string $pattern
234
     * @return string|array
235
     */
236
    public static function isNotRegExp ($x, string $pattern = null) {
237
        return static::compare($x, 'NOT REGEXP', $pattern);
238
    }
239
240
    /**
241
     * `$x REGEXP $pattern`
242
     *
243
     * @param string|array $x
244
     * @param string $pattern
245
     * @return string|array
246
     */
247
    public static function isRegExp ($x, string $pattern = null) {
248
        return static::compare($x, 'REGEXP', $pattern);
249
    }
250
251
    /**
252
     * Returns a `LIMIT ... [OFFSET ...]` clause,
253
     * or an empty string if the limit is zero.
254
     *
255
     * @param int $limit
256
     * @param int $offset
257
     * @return string
258
     */
259
    public static function limit (int $limit, int $offset = 0): string {
260
        if ($limit) {
261
            $limit = " LIMIT {$limit}";
262
            if ($offset) {
263
                $limit .= " OFFSET {$offset}";
264
            }
265
            return $limit;
266
        }
267
        return '';
268
    }
269
270
    /**
271
     * Returns an array of `?` placeholders.
272
     *
273
     * @param int|array|Countable $count
274
     * @return string[]
275
     */
276
    public static function marks ($count): array {
277
        if (is_array($count) or $count instanceof Countable) {
278
            $count = count($count);
279
        }
280
        return array_fill(0, $count, '?');
281
    }
282
283
    /**
284
     * `NOT($x)`
285
     *
286
     * @param string|array $x
287
     * @return string|array
288
     */
289
    public static function not ($x) {
290
        if (is_array($x)) {
291
            return array_map(__METHOD__, $x);
292
        }
293
        return "NOT({$x})";
294
    }
295
296
    /**
297
     * Returns an `ORDER BY` clause using the given order,
298
     * or an empty string if the order is empty.
299
     *
300
     * @param string $order
301
     * @return string
302
     */
303
    public static function order (string $order): string {
304
        if (strlen($order)) {
305
            return ' ORDER BY ' . $order;
306
        }
307
        return '';
308
    }
309
310
    /**
311
     * Returns a `SELECT` query string.
312
     *
313
     * @param string $table
314
     * @param array $columns String keys are used for aliasing.
315
     * @return string
316
     */
317
    public static function select (string $table, array $columns): string {
318
        $names = [];
319
        foreach ($columns as $alias => $name) {
320
            if (is_string($alias) and $alias !== $name) {
321
                $names[] = $name . ' AS ' . $alias;
322
            }
323
            else {
324
                $names[] = $name;
325
            }
326
        }
327
        return 'SELECT ' . implode(', ', $names) . ' FROM ' . $table;
328
    }
329
330
    /**
331
     * Converts an array of columns to `:named` placeholders for prepared queries.
332
     *
333
     * Qualified columns are slotted as `qualifier__column` (two underscores).
334
     *
335
     * @param string[] $columns
336
     * @return string[] `[column => :column]`
337
     */
338
    public static function slots (array $columns): array {
339
        $slots = [];
340
        foreach ($columns as $column) {
341
            $slots[(string)$column] = ':' . str_replace('.', '__', $column);
342
        }
343
        return $slots;
344
    }
345
346
    /**
347
     * Returns a `WHERE` clause for all conditions,
348
     * or an empty string if there are no conditions.
349
     *
350
     * @param array $conditions
351
     * @return string
352
     */
353
    public static function where (array $conditions): string {
354
        if (!empty($conditions)) {
355
            return ' WHERE ' . static::all($conditions);
356
        }
357
        return '';
358
    }
359
360
    final private function __construct () { }
361
}