Passed
Push — main ( c57f14...b113cb )
by Thierry
02:36
created

SelectInputTrait   A

Complexity

Total Complexity 31

Size/Duplication

Total Lines 146
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 59
c 1
b 0
f 0
dl 0
loc 146
rs 9.92
wmc 31

5 Methods

Rating   Name   Duplication   Size   Complexity  
A getSelectExpression() 0 25 6
B selectFieldIsValid() 0 11 7
B getWhereCondition() 0 25 8
A getMatchExpression() 0 10 2
B processSelectWhere() 0 15 8
1
<?php
2
3
namespace Lagdo\DbAdmin\Db\Traits;
4
5
use Lagdo\DbAdmin\Driver\Entity\IndexEntity;
6
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
7
8
use function preg_match;
9
use function implode;
10
use function in_array;
11
12
trait SelectInputTrait
13
{
14
    /**
15
     * Filter length value including enums
16
     *
17
     * @param string $length
18
     *
19
     * @return string
20
     */
21
    abstract public function processLength(string $length): string;
22
23
    /**
24
     * @param TableFieldEntity $field Single field from fields()
25
     * @param string $value
26
     * @param string $function
27
     *
28
     * @return string
29
     */
30
    abstract protected function getUnconvertedFieldValue(TableFieldEntity $field, string $value, string $function = ''): string;
31
32
    /**
33
     * @param array $value
34
     * @param array $fields
35
     *
36
     * @return string
37
     */
38
    private function getWhereCondition(array $value, array $fields): string
39
    {
40
        $op = $value['op'];
41
        $val = $value['val'];
42
        $col = $value['col'];
43
        if (preg_match('~IN$~', $op)) {
44
            $in = $this->processLength($val);
45
            return " $op " . ($in !== '' ? $in : '(NULL)');
46
        }
47
        if ($op === 'SQL') {
48
            return ' ' . $val; // SQL injection
49
        }
50
        if ($op === 'LIKE %%') {
51
            return ' LIKE ' . $this->getUnconvertedFieldValue($fields[$col], "%$val%");
52
        }
53
        if ($op === 'ILIKE %%') {
54
            return ' ILIKE ' . $this->getUnconvertedFieldValue($fields[$col], "%$val%");
55
        }
56
        if ($op === 'FIND_IN_SET') {
57
            return ')';
58
        }
59
        if (!preg_match('~NULL$~', $op)) {
60
            return " $op " . $this->getUnconvertedFieldValue($fields[$col], $val);
61
        }
62
        return " $op";
63
    }
64
65
    /**
66
     * @param TableFieldEntity $field
67
     * @param array $value
68
     *
69
     * @return bool
70
     */
71
    private function selectFieldIsValid(TableFieldEntity $field, array $value): bool
72
    {
73
        $op = $value['op'];
74
        $val = $value['val'];
75
        $in = preg_match('~IN$~', $op) ? ',' : '';
76
        return (preg_match('~^[-\d.' . $in . ']+$~', $val) ||
77
                !preg_match('~' . $this->driver->numberRegex() . '|bit~', $field->type)) &&
78
            (!preg_match("~[\x80-\xFF]~", $val) ||
79
                preg_match('~char|text|enum|set~', $field->type)) &&
80
            (!preg_match('~date|timestamp~', $field->type) ||
81
                preg_match('~^\d+-\d+-\d+~', $val));
82
    }
83
84
    /**
85
     * @param array $value
86
     * @param array $fields
87
     *
88
     * @return string
89
     */
90
    private function getSelectExpression(array $value, array $fields): string
91
    {
92
        $op = $value['op'];
93
        $col = $value['col'];
94
        $prefix = '';
95
        if ($op === 'FIND_IN_SET') {
96
            $prefix = $op .'(' . $this->driver->quote($value['val']) . ', ';
97
        }
98
        $condition = $this->getWhereCondition($value, $fields);
99
        if ($col !== '') {
100
            return $prefix . $this->driver->convertSearch($this->driver->escapeId($col),
101
                    $value, $fields[$col]) . $condition;
102
        }
103
        // find anywhere
104
        $clauses = [];
105
        foreach ($fields as $name => $field) {
106
            if ($this->selectFieldIsValid($field, $value)) {
107
                $clauses[] = $prefix . $this->driver->convertSearch($this->driver->escapeId($name),
108
                        $value, $field) . $condition;
109
            }
110
        }
111
        if (empty($clauses)) {
112
            return '1 = 0';
113
        }
114
        return '(' . implode(' OR ', $clauses) . ')';
115
    }
116
117
    /**
118
     * @param IndexEntity $index
119
     * @param int $i
120
     *
121
     * @return string
122
     */
123
    private function getMatchExpression(IndexEntity $index, int $i): string
124
    {
125
        $columns = array_map(function ($column) {
126
            return $this->driver->escapeId($column);
127
        }, $index->columns);
128
        $match = $this->driver->quote($this->input->values['fulltext'][$i]);
129
        if (isset($this->input->values['boolean'][$i])) {
130
            $match .= ' IN BOOLEAN MODE';
131
        }
132
        return 'MATCH (' . implode(', ', $columns) . ') AGAINST (' . $match . ')';
133
    }
134
135
    /**
136
     * Process search box in select
137
     *
138
     * @param array $fields
139
     * @param array $indexes
140
     *
141
     * @return array
142
     */
143
    public function processSelectWhere(array $fields, array $indexes): array
144
    {
145
        $expressions = [];
146
        foreach ($indexes as $i => $index) {
147
            if ($index->type === 'FULLTEXT' && $this->input->values['fulltext'][$i] !== '') {
148
                $expressions[] = $this->getMatchExpression($index, $i);
149
            }
150
        }
151
        foreach ((array) $this->input->values['where'] as $value) {
152
            if (($value['col'] !== '' ||  $value['val'] !== '') &&
153
                in_array($value['op'], $this->driver->operators())) {
154
                $expressions[] = $this->getSelectExpression($value, $fields);
155
            }
156
        }
157
        return $expressions;
158
    }
159
}
160