Passed
Push — main ( 364dc0...5d7b01 )
by Thierry
01:43
created

TableSelectAdmin::getSelectData()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 2
b 0
f 0
nc 1
nop 2
dl 0
loc 12
rs 10
1
<?php
2
3
namespace Lagdo\DbAdmin\DbAdmin;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
6
7
use Exception;
8
9
use function count;
10
use function str_replace;
11
use function compact;
12
use function preg_match;
13
use function microtime;
14
use function trim;
15
use function md5;
16
use function strlen;
17
use function strpos;
18
19
/**
20
 * Admin table select functions
21
 */
22
class TableSelectAdmin extends AbstractAdmin
23
{
24
    use Traits\TableSelectTrait;
1 ignored issue
show
introduced by
The trait Lagdo\DbAdmin\DbAdmin\Traits\TableSelectTrait requires some properties which are not provided by Lagdo\DbAdmin\DbAdmin\TableSelectAdmin: $privileges, $oid, $columns, $type
Loading history...
25
26
    /**
27
     * @param array $select
28
     * @param array $columns
29
     * @param array $indexes
30
     * @param int $limit
31
     * @param int $textLength
32
     * @param array $queryOptions
33
     *
34
     * @return array
35
     */
36
    private function getAllOptions(array $select, array $columns, array $indexes,
37
        int $limit, int $textLength, array $queryOptions): array
38
    {
39
        return [
40
            'columns' => $this->getColumnsOptions($select, $columns, $queryOptions),
41
            'filters' => $this->getFiltersOptions($columns, $indexes, $queryOptions),
42
            'sorting' => $this->getSortingOptions($columns, $queryOptions),
43
            'limit' => $this->getLimitOptions($limit),
44
            'length' => $this->getLengthOptions($textLength),
45
            // 'action' => $this->getActionOptions($indexes),
46
        ];
47
    }
48
49
    /**
50
     * Get required data for create/update on tables
51
     *
52
     * @param string $table The table name
53
     * @param array $queryOptions The query options
54
     *
55
     * @return array
56
     * @throws Exception
57
     */
58
    private function prepareSelect(string $table, array &$queryOptions = []): array
59
    {
60
        $page = $this->setDefaultOptions($queryOptions);
61
        $this->util->input()->setValues($queryOptions);
0 ignored issues
show
Bug introduced by
The method setValues() does not exist on Lagdo\DbAdmin\Driver\InputInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

61
        $this->util->input()->/** @scrutinizer ignore-call */ setValues($queryOptions);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
62
63
        // From select.inc.php
64
        $fields = $this->driver->fields($table);
65
        list(, $columns, $textLength) = $this->getFieldsOptions($fields);
66
        if (!$columns && $this->driver->support("table")) {
67
            throw new Exception($this->trans->lang('Unable to select the table') .
68
                ($fields ? "." : ": " . $this->driver->error()));
69
        }
70
71
        $indexes = $this->driver->indexes($table);
72
        $foreignKeys = $this->admin->columnForeignKeys($table);
73
        list($select, $group) = $this->util->processSelectColumns();
74
        $where = $this->util->processSelectSearch($fields, $indexes);
75
        $order = $this->util->processSelectOrder();
76
        $limit = $this->util->processSelectLimit();
77
        $tableStatus = $this->driver->tableStatusOrName($table);
78
        $unselected = $this->setPrimaryKey($indexes, $select, $tableStatus);
79
        $tableName = $this->util->tableName($tableStatus);
80
81
        // $set = null;
82
        // if(isset($rights["insert"]) || !support("table")) {
83
        //     $set = "";
84
        //     foreach((array) $queryOptions["where"] as $val) {
85
        //         if($foreignKeys[$val["col"]] && count($foreignKeys[$val["col"]]) == 1 && ($val["op"] == "="
86
        //             || (!$val["op"] && !preg_match('~[_%]~', $val["val"])) // LIKE in Editor
87
        //         )) {
88
        //             $set .= "&set" . urlencode("[" . $this->util->bracketEscape($val["col"]) . "]") . "=" . urlencode($val["val"]);
89
        //         }
90
        //     }
91
        // }
92
        // $this->util->selectLinks($tableStatus, $set);
93
94
        // if($page == "last")
95
        // {
96
        //     $isGroup = count($group) < count($select);
97
        //     $found_rows = $this->driver->result($this->driver->sqlForRowCount($table, $where, $isGroup, $group));
98
        //     $page = \floor(\max(0, $found_rows - 1) / $limit);
99
        // }
100
101
        $options = $this->getAllOptions($select, $columns, $indexes, $limit, $textLength, $queryOptions);
102
        $entity = $this->getSelectEntity($table, $columns, $fields, $select, $group, $where, $order, $unselected, $limit, $page);
103
        $query = $this->driver->buildSelectQuery($entity);
104
        // From adminer.inc.php
105
        $query = str_replace("\n", " ", $query);
106
107
        return [$options, $query, $select, $fields, $foreignKeys, $columns, $indexes,
108
            $where, $group, $order, $limit, $page, $textLength, $tableName, $unselected];
109
    }
110
111
    /**
112
     * Get required data for create/update on tables
113
     *
114
     * @param string $table The table name
115
     * @param array $queryOptions The query options
116
     *
117
     * @return array
118
     * @throws Exception
119
     */
120
    public function getSelectData(string $table, array $queryOptions = []): array
121
    {
122
        list($options, $query) = $this->prepareSelect($table, $queryOptions);
123
124
        $query = $this->util->html($query);
125
        $mainActions = [
126
            'select-exec' => $this->trans->lang('Execute'),
127
            'insert-table' => $this->trans->lang('New item'),
128
            'select-back' => $this->trans->lang('Back'),
129
        ];
130
131
        return compact('mainActions', 'options', 'query');
132
    }
133
134
    /**
135
     * @param string $query
136
     * @param int $page
137
     *
138
     * @return array
139
     */
140
    private function executeQuery(string $query, int $page): array
141
    {
142
        // From driver.inc.php
143
        $start = microtime(true);
144
        $statement = $this->driver->execute($query);
145
        // From adminer.inc.php
146
        $duration = $this->trans->formatTime($start); // Compute and format the duration
147
148
        if (!$statement) {
149
            return ['error' => $this->driver->error()];
150
        }
151
        // From select.inc.php
152
        $rows = [];
153
        while (($row = $statement->fetchAssoc())) {
154
            if ($page && $this->driver->jush() == "oracle") {
155
                unset($row["RNUM"]);
156
            }
157
            $rows[] = $row;
158
        }
159
160
        return [$rows, $duration];
161
    }
162
163
    /**
164
     * @param array $rows
165
     * @param array $select
166
     * @param array $fields
167
     * @param array $unselected
168
     * @param array $queryOptions
169
     *
170
     * @return array
171
     */
172
    private function getResultHeaders(array $rows, array $select, array $fields, array $unselected, array $queryOptions): array
173
    {
174
        // Results headers
175
        $headers = [
176
            '', // !$group && $select ? '' : lang('Modify');
177
        ];
178
        $names = [];
179
        // $functions = [];
180
        reset($select);
181
        $rank = 1;
182
        foreach ($rows[0] as $key => $value) {
183
            $header = [];
184
            if (!isset($unselected[$key])) {
185
                $value = $queryOptions["columns"][key($select)] ?? [];
186
                $fun = $value["fun"] ?? '';
187
                $field = $fields[$select ? ($value ? $value["col"] : current($select)) : $key];
188
                $name = ($field ? $this->util->fieldName($field, $rank) : ($fun ? "*" : $key));
189
                $header = compact('value', 'field', 'name');
190
                if ($name != "") {
191
                    $rank++;
192
                    $names[$key] = $name;
193
                    $column = $this->driver->escapeId($key);
194
                    // $href = remove_from_uri('(order|desc)[^=]*|page') . '&order%5B0%5D=' . urlencode($key);
195
                    // $desc = "&desc%5B0%5D=1";
196
                    $header['column'] = $column;
197
                    $header['key'] = $this->util->html($this->util->bracketEscape($key));
198
                    $header['sql'] = $this->util->applySqlFunction($fun, $name); //! columns looking like functions
199
                }
200
                // $functions[$key] = $fun;
201
                next($select);
202
            }
203
            $headers[] = $header;
204
        }
205
        return [$headers, $names];
206
    }
207
208
    /**
209
     * @param array $rows
210
     * @param array $queryOptions
211
     *
212
     * @return array
213
     */
214
    /*private function getValuesLengths(array $rows, array $queryOptions): array
215
    {
216
         $lengths = [];
217
         if($queryOptions["modify"])
218
         {
219
             foreach($rows as $row)
220
             {
221
                 foreach($row as $key => $value)
222
                 {
223
                     $lengths[$key] = \max($lengths[$key], \min(40, strlen(\utf8_decode($value))));
224
                 }
225
             }
226
         }
227
         return $lengths;
228
    }*/
229
230
    /**
231
     * @param array $row
232
     * @param array $indexes
233
     *
234
     * @return array
235
     */
236
    private function getUniqueIds(array $row, array $indexes): array
237
    {
238
        $uniqueIds = $this->util->uniqueIds($row, $indexes);
239
        if (empty($uniqueIds)) {
240
            $pattern = '~^(COUNT\((\*|(DISTINCT )?`(?:[^`]|``)+`)\)|(AVG|GROUP_CONCAT|MAX|MIN|SUM)\(`(?:[^`]|``)+`\))$~';
241
            foreach ($row as $key => $value) {
242
                if (!preg_match($pattern, $key)) {
243
                    //! columns looking like functions
244
                    $uniqueIds[$key] = $value;
245
                }
246
            }
247
        }
248
        return $uniqueIds;
249
    }
250
251
    /**
252
     * @param array $row
253
     * @param array $fields
254
     * @param array $indexes
255
     *
256
     * @return array
257
     */
258
    private function getRowIds(array $row, array $fields, array $indexes): array
259
    {
260
        $uniqueIds = $this->getUniqueIds($row, $indexes);
261
        // Unique identifier to edit returned data.
262
        // $unique_idf = "";
263
        $rowIds = ['where' => [], 'null' => []];
264
        foreach ($uniqueIds as $key => $value) {
265
            $key = trim($key);
266
            $type = '';
267
            $collation = '';
268
            if (isset($fields[$key])) {
269
                $type = $fields[$key]->type;
270
                $collation = $fields[$key]->collation;
271
            }
272
            if (($this->driver->jush() == "sql" || $this->driver->jush() == "pgsql") &&
273
                preg_match('~char|text|enum|set~', $type) && strlen($value) > 64) {
274
                $key = (strpos($key, '(') ? $key : $this->driver->escapeId($key)); //! columns looking like functions
275
                $key = "MD5(" . ($this->driver->jush() != 'sql' || preg_match("~^utf8~", $collation) ?
276
                        $key : "CONVERT($key USING " . $this->driver->charset() . ")") . ")";
277
                $value = md5($value);
278
            }
279
            if ($value !== null) {
280
                $rowIds['where'][$this->util->bracketEscape($key)] = $value;
281
            } else {
282
                $rowIds['null'][] = $this->util->bracketEscape($key);
283
            }
284
            // $unique_idf .= "&" . ($value !== null ? \urlencode("where[" . $this->util->bracketEscape($key) . "]") .
285
            //     "=" . \urlencode($value) : \urlencode("null[]") . "=" . \urlencode($key));
286
        }
287
        return $rowIds;
288
    }
289
290
    /**
291
     * @param array $row
292
     * @param array $fields
293
     * @param array $names
294
     * @param int $textLength
295
     *
296
     * @return array
297
     */
298
    private function getRowColumns(array $row, array $fields, array $names, int $textLength): array
299
    {
300
        $cols = [];
301
        foreach ($row as $key => $value) {
302
            if (isset($names[$key])) {
303
                $field = $fields[$key] ?? new TableFieldEntity();
304
                $value = $this->driver->value($value, $field);
305
                /*if ($value != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) {
306
                    //! filled e-mails can be contained on other pages
307
                    $email_fields[$key] = ($this->util->isMail($value) ? $names[$key] : "");
308
                }*/
309
                $link = "";
310
                $cols[] = [
311
                    // 'id',
312
                    'text' => preg_match('~text|lob~', $field->type),
313
                    'value' => $this->util->selectValue($value, $link, $field, $textLength),
314
                    // 'editable' => false,
315
                ];
316
            }
317
        }
318
        return $cols;
319
    }
320
321
    /**
322
     * Get required data for create/update on tables
323
     *
324
     * @param string $table The table name
325
     * @param array $queryOptions The query options
326
     *
327
     * @return array
328
     * @throws Exception
329
     */
330
    public function execSelect(string $table, array $queryOptions): array
331
    {
332
        list(, $query, $select, $fields, , , $indexes, $where, $group, , $limit, $page,
333
            $textLength, , $unselected) = $this->prepareSelect($table, $queryOptions);
334
335
        list($rows, $duration) = $this->executeQuery($query, $page);
336
        if (!$rows) {
337
            return ['error' => $this->trans->lang('No rows.')];
338
        }
339
        // $backward_keys = $this->driver->backwardKeys($table, $tableName);
340
        // lengths = $this->getValuesLengths($rows, $queryOptions);
341
342
        list($headers, $names) = $this->getResultHeaders($rows, $select, $fields, $unselected, $queryOptions);
343
344
        $results = [];
345
        foreach ($rows as $row) {
346
            // Unique identifier to edit returned data.
347
            $rowIds = $this->getRowIds($row, $fields, $indexes);
348
            $cols = $this->getRowColumns($row, $fields, $names, $textLength);
349
            $results[] = ['ids' => $rowIds, 'cols' => $cols];
350
        }
351
352
        $isGroup = count($group) < count($select);
353
        $total = $this->driver->result($this->driver->sqlForRowCount($table, $where, $isGroup, $group));
354
355
        $rows = $results;
356
        $error = null;
357
        return compact('duration', 'headers', 'query', 'rows', 'limit', 'total', 'error');
358
    }
359
}
360