Passed
Push — main ( 561b46...364dc0 )
by Thierry
02:06
created

TableSelectAdmin::getSelectEntity()   B

Complexity

Conditions 10
Paths 18

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 10
eloc 18
c 2
b 0
f 0
nc 18
nop 10
dl 0
loc 30
rs 7.6666

How to fix   Complexity    Many Parameters   

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:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
namespace Lagdo\DbAdmin\DbAdmin;
4
5
use Lagdo\DbAdmin\Driver\Entity\TableFieldEntity;
6
use Lagdo\DbAdmin\Driver\Entity\TableSelectEntity;
7
8
use Exception;
9
10
use function intval;
11
use function count;
12
use function str_replace;
13
use function compact;
14
use function preg_match;
15
use function microtime;
16
use function html_entity_decode;
17
use function strip_tags;
18
use function array_flip;
19
use function in_array;
20
use function trim;
21
use function md5;
22
use function strlen;
23
use function strpos;
24
25
/**
26
 * Admin table select functions
27
 */
28
class TableSelectAdmin extends AbstractAdmin
29
{
30
    /**
31
     * Print columns box in select
32
     * @param array $select Result of processSelectColumns()[0]
33
     * @param array $columns Selectable columns
34
     * @param array $options
35
     * @return array
36
     */
37
    private function getColumnsOptions(array $select, array $columns, array $options): array
38
    {
39
        return [
40
            'select' => $select,
41
            'values' => (array)$options["columns"],
42
            'columns' => $columns,
43
            'functions' => $this->driver->functions(),
44
            'grouping' => $this->driver->grouping(),
45
        ];
46
    }
47
48
    /**
49
     * Print search box in select
50
     *
51
     * @param array $columns Selectable columns
52
     * @param array $indexes
53
     * @param array $options
54
     *
55
     * @return array
56
     */
57
    private function getFiltersOptions(array $columns, array $indexes, array $options): array
58
    {
59
        $fulltexts = [];
60
        foreach ($indexes as $i => $index) {
61
            $fulltexts[$i] = $index->type == "FULLTEXT" ? $this->util->html($options["fulltext"][$i]) : '';
62
        }
63
        return [
64
            // 'where' => $where,
65
            'values' => (array)$options["where"],
66
            'columns' => $columns,
67
            'indexes' => $indexes,
68
            'operators' => $this->driver->operators(),
69
            'fulltexts' => $fulltexts,
70
        ];
71
    }
72
73
    /**
74
     * Print order box in select
75
     *
76
     * @param array $columns Selectable columns
77
     * @param array $options
78
     *
79
     * @return array
80
     */
81
    private function getSortingOptions(array $columns, array $options): array
82
    {
83
        $values = [];
84
        $descs = (array)$options["desc"];
85
        foreach ((array)$options["order"] as $key => $value) {
86
            $values[] = [
87
                'col' => $value,
88
                'desc' => $descs[$key] ?? 0,
89
            ];
90
        }
91
        return [
92
            // 'order' => $order,
93
            'values' => $values,
94
            'columns' => $columns,
95
        ];
96
    }
97
98
    /**
99
     * Print limit box in select
100
     *
101
     * @param string $limit Result of processSelectLimit()
102
     *
103
     * @return array
104
     */
105
    private function getLimitOptions(string $limit): array
106
    {
107
        return ['value' => $this->util->html($limit)];
108
    }
109
110
    /**
111
     * Print text length box in select
112
     *
113
     * @param int $textLength Result of processSelectLength()
114
     *
115
     * @return array
116
     */
117
    private function getLengthOptions($textLength): array
118
    {
119
        return [
120
            'value' => $textLength === 0 ? 0 : $this->util->html($textLength),
121
        ];
122
    }
123
124
    /**
125
     * Print action box in select
126
     *
127
     * @param array $indexes
128
     *
129
     * @return array
130
     */
131
    // private function getActionOptions(array $indexes)
132
    // {
133
    //     $columns = [];
134
    //     foreach ($indexes as $index) {
135
    //         $current_key = \reset($index->columns);
136
    //         if ($index->type != "FULLTEXT" && $current_key) {
137
    //             $columns[$current_key] = 1;
138
    //         }
139
    //     }
140
    //     $columns[""] = 1;
141
    //     return ['columns' => $columns];
142
    // }
143
144
    /**
145
     * Print command box in select
146
     *
147
     * @return bool whether to print default commands
148
     */
149
    // private function getCommandOptions()
150
    // {
151
    //     return !$this->driver->isInformationSchema($this->driver->database());
152
    // }
153
154
    /**
155
     * Print import box in select
156
     *
157
     * @return bool whether to print default import
158
     */
159
    // private function getImportOptions()
160
    // {
161
    //     return !$this->driver->isInformationSchema($this->driver->database());
162
    // }
163
164
    /**
165
     * Print extra text in the end of a select form
166
     *
167
     * @param array $emailFields Fields holding e-mails
168
     * @param array $columns Selectable columns
169
     *
170
     * @return array
171
     */
172
    // private function getEmailOptions(array $emailFields, array $columns)
173
    // {
174
    // }
175
176
    /**
177
     * @param array $queryOptions
178
     *
179
     * @return int
180
     */
181
    private function setDefaultOptions(array &$queryOptions): int
182
    {
183
        $defaultOptions = [
184
            'columns' => [],
185
            'where' => [],
186
            'order' => [],
187
            'desc' => [],
188
            'fulltext' => [],
189
            'limit' => '50',
190
            'text_length' => '100',
191
            'page' => '1',
192
        ];
193
        foreach ($defaultOptions as $name => $value) {
194
            if (!isset($queryOptions[$name])) {
195
                $queryOptions[$name] = $value;
196
            }
197
        }
198
        $page = intval($queryOptions['page']);
199
        if ($page > 0) {
200
            $page -= 1; // Page numbers start at 0 here, instead of 1.
201
        }
202
        $queryOptions['page'] = $page;
203
        return $page;
204
    }
205
206
    /**
207
     * @param array $fields
208
     *
209
     * @return array
210
     */
211
    private function getFieldsOptions(array $fields): array
212
    {
213
        $rights = []; // privilege => 0
214
        $columns = []; // selectable columns
215
        $textLength = 0;
216
        foreach ($fields as $key => $field) {
217
            $name = $this->util->fieldName($field);
218
            if (isset($field->privileges["select"]) && $name != "") {
219
                $columns[$key] = html_entity_decode(strip_tags($name), ENT_QUOTES);
220
                if ($this->util->isShortable($field)) {
221
                    $textLength = $this->util->processSelectLength();
222
                }
223
            }
224
            $rights[] = $field->privileges;
225
        }
226
        return [$rights, $columns, $textLength];
227
    }
228
229
    /**
230
     * @param array $indexes
231
     * @param array $select
232
     * @param mixed $tableStatus
233
     *
234
     * @return array
235
     */
236
    private function setPrimaryKey(array &$indexes, array $select, $tableStatus): array
237
    {
238
        $primary = null;
239
        $unselected = [];
240
        foreach ($indexes as $index) {
241
            if ($index->type == "PRIMARY") {
242
                $primary = array_flip($index->columns);
243
                $unselected = ($select ? $primary : []);
244
                foreach ($unselected as $key => $val) {
245
                    if (in_array($this->driver->escapeId($key), $select)) {
246
                        unset($unselected[$key]);
247
                    }
248
                }
249
                break;
250
            }
251
        }
252
253
        $oid = $tableStatus->oid;
254
        if ($oid && !$primary) {
255
            /*$primary = */$unselected = [$oid => 0];
256
            $indexes[] = ["type" => "PRIMARY", "columns" => [$oid]];
257
        }
258
259
        return $unselected;
260
    }
261
262
    /**
263
     * @param array $select
264
     * @param array $columns
265
     * @param array $indexes
266
     * @param int $limit
267
     * @param int $textLength
268
     * @param array $queryOptions
269
     *
270
     * @return array
271
     */
272
    private function getAllOptions(array $select, array $columns, array $indexes,
273
        int $limit, int $textLength, array $queryOptions): array
274
    {
275
        return [
276
            'columns' => $this->getColumnsOptions($select, $columns, $queryOptions),
277
            'filters' => $this->getFiltersOptions($columns, $indexes, $queryOptions),
278
            'sorting' => $this->getSortingOptions($columns, $queryOptions),
279
            'limit' => $this->getLimitOptions($limit),
280
            'length' => $this->getLengthOptions($textLength),
281
            // 'action' => $this->getActionOptions($indexes),
282
        ];
283
    }
284
285
    /**
286
     * @param string $table
287
     * @param array $columns
288
     * @param array $fields
289
     * @param array $select
290
     * @param array $group
291
     * @param array $where
292
     * @param array $order
293
     * @param array $unselected
294
     * @param int $limit
295
     * @param int $page
296
     *
297
     * @return TableSelectEntity
298
     */
299
    private function getSelectEntity(string $table, array $columns, array $fields, array $select,
300
        array $group, array $where, array $order, array $unselected, int $limit, int $page): TableSelectEntity
301
    {
302
        $select2 = $select;
303
        $group2 = $group;
304
        if (empty($select2)) {
305
            $select2[] = "*";
306
            $convert_fields = $this->driver->convertFields($columns, $fields, $select);
307
            if ($convert_fields) {
308
                $select2[] = \substr($convert_fields, 2);
309
            }
310
        }
311
        foreach ($select as $key => $val) {
312
            $field = $fields[$this->driver->unescapeId($val)] ?? null;
313
            if ($field && ($as = $this->driver->convertField($field))) {
314
                $select2[$key] = "$as AS $val";
315
            }
316
        }
317
        $isGroup = count($group) < count($select);
318
        if (!$isGroup && !empty($unselected)) {
319
            foreach ($unselected as $key => $val) {
320
                $select2[] = $this->driver->escapeId($key);
321
                if (!empty($group2)) {
322
                    $group2[] = $this->driver->escapeId($key);
323
                }
324
            }
325
        }
326
327
        // From driver.inc.php
328
        return new TableSelectEntity($table, $select2, $where, $group2, $order, $limit, $page);
329
    }
330
331
    /**
332
     * Get required data for create/update on tables
333
     *
334
     * @param string $table The table name
335
     * @param array $queryOptions The query options
336
     *
337
     * @return array
338
     * @throws Exception
339
     */
340
    private function prepareSelect(string $table, array &$queryOptions = []): array
341
    {
342
        $page = $this->setDefaultOptions($queryOptions);
343
        $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

343
        $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...
344
345
        // From select.inc.php
346
        $fields = $this->driver->fields($table);
347
        list(, $columns, $textLength) = $this->getFieldsOptions($fields);
348
        if (!$columns && $this->driver->support("table")) {
349
            throw new Exception($this->trans->lang('Unable to select the table') .
350
                ($fields ? "." : ": " . $this->driver->error()));
351
        }
352
353
        $indexes = $this->driver->indexes($table);
354
        $foreignKeys = $this->admin->columnForeignKeys($table);
355
        list($select, $group) = $this->util->processSelectColumns();
356
        $where = $this->util->processSelectSearch($fields, $indexes);
357
        $order = $this->util->processSelectOrder();
358
        $limit = $this->util->processSelectLimit();
359
        $tableStatus = $this->driver->tableStatusOrName($table);
360
        $unselected = $this->setPrimaryKey($indexes, $select, $tableStatus);
361
        $tableName = $this->util->tableName($tableStatus);
362
363
        // $set = null;
364
        // if(isset($rights["insert"]) || !support("table")) {
365
        //     $set = "";
366
        //     foreach((array) $queryOptions["where"] as $val) {
367
        //         if($foreignKeys[$val["col"]] && count($foreignKeys[$val["col"]]) == 1 && ($val["op"] == "="
368
        //             || (!$val["op"] && !preg_match('~[_%]~', $val["val"])) // LIKE in Editor
369
        //         )) {
370
        //             $set .= "&set" . urlencode("[" . $this->util->bracketEscape($val["col"]) . "]") . "=" . urlencode($val["val"]);
371
        //         }
372
        //     }
373
        // }
374
        // $this->util->selectLinks($tableStatus, $set);
375
376
        // if($page == "last")
377
        // {
378
        //     $isGroup = count($group) < count($select);
379
        //     $found_rows = $this->driver->result($this->driver->sqlForRowCount($table, $where, $isGroup, $group));
380
        //     $page = \floor(\max(0, $found_rows - 1) / $limit);
381
        // }
382
383
        $options = $this->getAllOptions($select, $columns, $indexes, $limit, $textLength, $queryOptions);
384
        $entity = $this->getSelectEntity($table, $columns, $fields, $select, $group, $where, $order, $unselected, $limit, $page);
385
        $query = $this->driver->buildSelectQuery($entity);
386
        // From adminer.inc.php
387
        $query = str_replace("\n", " ", $query);
388
389
        return [$options, $query, $select, $fields, $foreignKeys, $columns, $indexes,
390
            $where, $group, $order, $limit, $page, $textLength, $tableName, $unselected];
391
    }
392
393
    /**
394
     * Get required data for create/update on tables
395
     *
396
     * @param string $table The table name
397
     * @param array $queryOptions The query options
398
     *
399
     * @return array
400
     * @throws Exception
401
     */
402
    public function getSelectData(string $table, array $queryOptions = []): array
403
    {
404
        list($options, $query) = $this->prepareSelect($table, $queryOptions);
405
406
        $query = $this->util->html($query);
407
        $mainActions = [
408
            'select-exec' => $this->trans->lang('Execute'),
409
            'insert-table' => $this->trans->lang('New item'),
410
            'select-back' => $this->trans->lang('Back'),
411
        ];
412
413
        return compact('mainActions', 'options', 'query');
414
    }
415
416
    /**
417
     * @param string $query
418
     * @param int $page
419
     *
420
     * @return array
421
     */
422
    private function executeQuery(string $query, int $page): array
423
    {
424
        // From driver.inc.php
425
        $start = microtime(true);
426
        $statement = $this->driver->execute($query);
427
        // From adminer.inc.php
428
        $duration = $this->trans->formatTime($start); // Compute and format the duration
429
430
        if (!$statement) {
431
            return ['error' => $this->driver->error()];
432
        }
433
        // From select.inc.php
434
        $rows = [];
435
        while (($row = $statement->fetchAssoc())) {
436
            if ($page && $this->driver->jush() == "oracle") {
437
                unset($row["RNUM"]);
438
            }
439
            $rows[] = $row;
440
        }
441
442
        return [$rows, $duration];
443
    }
444
445
    /**
446
     * @param array $rows
447
     * @param array $select
448
     * @param array $fields
449
     * @param array $unselected
450
     * @param array $queryOptions
451
     *
452
     * @return array
453
     */
454
    private function getResultHeaders(array $rows, array $select, array $fields, array $unselected, array $queryOptions): array
455
    {
456
        // Results headers
457
        $headers = [
458
            '', // !$group && $select ? '' : lang('Modify');
459
        ];
460
        $names = [];
461
        // $functions = [];
462
        reset($select);
463
        $rank = 1;
464
        foreach ($rows[0] as $key => $value) {
465
            $header = [];
466
            if (!isset($unselected[$key])) {
467
                $value = $queryOptions["columns"][key($select)] ?? [];
468
                $fun = $value["fun"] ?? '';
469
                $field = $fields[$select ? ($value ? $value["col"] : current($select)) : $key];
470
                $name = ($field ? $this->util->fieldName($field, $rank) : ($fun ? "*" : $key));
471
                $header = compact('value', 'field', 'name');
472
                if ($name != "") {
473
                    $rank++;
474
                    $names[$key] = $name;
475
                    $column = $this->driver->escapeId($key);
476
                    // $href = remove_from_uri('(order|desc)[^=]*|page') . '&order%5B0%5D=' . urlencode($key);
477
                    // $desc = "&desc%5B0%5D=1";
478
                    $header['column'] = $column;
479
                    $header['key'] = $this->util->html($this->util->bracketEscape($key));
480
                    $header['sql'] = $this->util->applySqlFunction($fun, $name); //! columns looking like functions
481
                }
482
                // $functions[$key] = $fun;
483
                next($select);
484
            }
485
            $headers[] = $header;
486
        }
487
        return [$headers, $names];
488
    }
489
490
    /**
491
     * @param array $rows
492
     * @param array $queryOptions
493
     *
494
     * @return array
495
     */
496
    /*private function getValuesLengths(array $rows, array $queryOptions): array
497
    {
498
         $lengths = [];
499
         if($queryOptions["modify"])
500
         {
501
             foreach($rows as $row)
502
             {
503
                 foreach($row as $key => $value)
504
                 {
505
                     $lengths[$key] = \max($lengths[$key], \min(40, strlen(\utf8_decode($value))));
506
                 }
507
             }
508
         }
509
         return $lengths;
510
    }*/
511
512
    /**
513
     * @param array $row
514
     * @param array $indexes
515
     *
516
     * @return array
517
     */
518
    private function getUniqueIds(array $row, array $indexes): array
519
    {
520
        $uniqueIds = $this->util->uniqueIds($row, $indexes);
521
        if (empty($uniqueIds)) {
522
            $pattern = '~^(COUNT\((\*|(DISTINCT )?`(?:[^`]|``)+`)\)|(AVG|GROUP_CONCAT|MAX|MIN|SUM)\(`(?:[^`]|``)+`\))$~';
523
            foreach ($row as $key => $value) {
524
                if (!preg_match($pattern, $key)) {
525
                    //! columns looking like functions
526
                    $uniqueIds[$key] = $value;
527
                }
528
            }
529
        }
530
        return $uniqueIds;
531
    }
532
533
    /**
534
     * @param array $row
535
     * @param array $fields
536
     * @param array $indexes
537
     *
538
     * @return array
539
     */
540
    private function getRowIds(array $row, array $fields, array $indexes): array
541
    {
542
        $uniqueIds = $this->getUniqueIds($row, $indexes);
543
        // Unique identifier to edit returned data.
544
        // $unique_idf = "";
545
        $rowIds = ['where' => [], 'null' => []];
546
        foreach ($uniqueIds as $key => $value) {
547
            $key = trim($key);
548
            $type = '';
549
            $collation = '';
550
            if (isset($fields[$key])) {
551
                $type = $fields[$key]->type;
552
                $collation = $fields[$key]->collation;
553
            }
554
            if (($this->driver->jush() == "sql" || $this->driver->jush() == "pgsql") &&
555
                preg_match('~char|text|enum|set~', $type) && strlen($value) > 64) {
556
                $key = (strpos($key, '(') ? $key : $this->driver->escapeId($key)); //! columns looking like functions
557
                $key = "MD5(" . ($this->driver->jush() != 'sql' || preg_match("~^utf8~", $collation) ?
558
                        $key : "CONVERT($key USING " . $this->driver->charset() . ")") . ")";
559
                $value = md5($value);
560
            }
561
            if ($value !== null) {
562
                $rowIds['where'][$this->util->bracketEscape($key)] = $value;
563
            } else {
564
                $rowIds['null'][] = $this->util->bracketEscape($key);
565
            }
566
            // $unique_idf .= "&" . ($value !== null ? \urlencode("where[" . $this->util->bracketEscape($key) . "]") .
567
            //     "=" . \urlencode($value) : \urlencode("null[]") . "=" . \urlencode($key));
568
        }
569
        return $rowIds;
570
    }
571
572
    /**
573
     * @param array $row
574
     * @param array $fields
575
     * @param array $names
576
     * @param int $textLength
577
     *
578
     * @return array
579
     */
580
    private function getRowColumns(array $row, array $fields, array $names, int $textLength): array
581
    {
582
        $cols = [];
583
        foreach ($row as $key => $value) {
584
            if (isset($names[$key])) {
585
                $field = $fields[$key] ?? new TableFieldEntity();
586
                $value = $this->driver->value($value, $field);
587
                /*if ($value != "" && (!isset($email_fields[$key]) || $email_fields[$key] != "")) {
588
                    //! filled e-mails can be contained on other pages
589
                    $email_fields[$key] = ($this->util->isMail($value) ? $names[$key] : "");
590
                }*/
591
                $link = "";
592
                $cols[] = [
593
                    // 'id',
594
                    'text' => preg_match('~text|lob~', $field->type),
595
                    'value' => $this->util->selectValue($value, $link, $field, $textLength),
596
                    // 'editable' => false,
597
                ];
598
            }
599
        }
600
        return $cols;
601
    }
602
603
    /**
604
     * Get required data for create/update on tables
605
     *
606
     * @param string $table The table name
607
     * @param array $queryOptions The query options
608
     *
609
     * @return array
610
     * @throws Exception
611
     */
612
    public function execSelect(string $table, array $queryOptions): array
613
    {
614
        list(, $query, $select, $fields, , , $indexes, $where, $group, , $limit, $page,
615
            $textLength, , $unselected) = $this->prepareSelect($table, $queryOptions);
616
617
        list($rows, $duration) = $this->executeQuery($query, $page);
618
        if (!$rows) {
619
            return ['error' => $this->trans->lang('No rows.')];
620
        }
621
        // $backward_keys = $this->driver->backwardKeys($table, $tableName);
622
        // lengths = $this->getValuesLengths($rows, $queryOptions);
623
624
        list($headers, $names) = $this->getResultHeaders($rows, $select, $fields, $unselected, $queryOptions);
625
626
        $results = [];
627
        foreach ($rows as $row) {
628
            // Unique identifier to edit returned data.
629
            $rowIds = $this->getRowIds($row, $fields, $indexes);
630
            $cols = $this->getRowColumns($row, $fields, $names, $textLength);
631
            $results[] = ['ids' => $rowIds, 'cols' => $cols];
632
        }
633
634
        $isGroup = count($group) < count($select);
635
        $total = $this->driver->result($this->driver->sqlForRowCount($table, $where, $isGroup, $group));
636
637
        $rows = $results;
638
        $error = null;
639
        return compact('duration', 'headers', 'query', 'rows', 'limit', 'total', 'error');
640
    }
641
}
642