Passed
Push — master ( 3bb133...b97460 )
by Maurício
07:10
created

StructureController::displayTableList()   F

Complexity

Conditions 50
Paths > 20000

Size

Total Lines 328
Code Lines 233

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 233
dl 0
loc 328
rs 0
c 0
b 0
f 0
cc 50
nc 1152002
nop 0

How to fix   Long Method    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
/* vim: set expandtab sw=4 ts=4 sts=4: */
3
/**
4
 * Holds the PhpMyAdmin\Controllers\Database\StructureController
5
 *
6
 * @package PhpMyAdmin\Controllers
7
 */
8
declare(strict_types=1);
9
10
namespace PhpMyAdmin\Controllers\Database;
11
12
use PhpMyAdmin\Charsets;
13
use PhpMyAdmin\Config\PageSettings;
14
use PhpMyAdmin\Controllers\DatabaseController;
15
use PhpMyAdmin\Core;
16
use PhpMyAdmin\DatabaseInterface;
17
use PhpMyAdmin\Display\CreateTable;
18
use PhpMyAdmin\Message;
19
use PhpMyAdmin\RecentFavoriteTable;
20
use PhpMyAdmin\Relation;
21
use PhpMyAdmin\Replication;
22
use PhpMyAdmin\Response;
23
use PhpMyAdmin\Sanitize;
24
use PhpMyAdmin\Template;
25
use PhpMyAdmin\Tracker;
26
use PhpMyAdmin\Url;
27
use PhpMyAdmin\Util;
28
29
/**
30
 * Handles database structure logic
31
 *
32
 * @package PhpMyAdmin\Controllers
33
 */
34
class StructureController extends DatabaseController
35
{
36
    /**
37
     * @var int Number of tables
38
     */
39
    protected $_num_tables;
40
    /**
41
     * @var int Current position in the list
42
     */
43
    protected $_pos;
44
    /**
45
     * @var bool DB is information_schema
46
     */
47
    protected $_db_is_system_schema;
48
    /**
49
     * @var int Number of tables
50
     */
51
    protected $_total_num_tables;
52
    /**
53
     * @var array Tables in the database
54
     */
55
    protected $_tables;
56
    /**
57
     * @var bool whether stats show or not
58
     */
59
    protected $_is_show_stats;
60
61
    /**
62
     * @var Relation
63
     */
64
    private $relation;
65
66
    /**
67
     * @var Replication
68
     */
69
    private $replication;
70
71
    /**
72
     * Constructor
73
     *
74
     * @param Response          $response Response object
75
     * @param DatabaseInterface $dbi      DatabaseInterface object
76
     * @param string            $db       Database name
77
     */
78
    public function __construct($response, $dbi, $db)
79
    {
80
        parent::__construct($response, $dbi, $db);
81
        $this->relation = new Relation($dbi);
82
        $this->replication = new Replication();
83
    }
84
85
    /**
86
     * Retrieves databse information for further use
87
     *
88
     * @param string $sub_part Page part name
89
     *
90
     * @return void
91
     */
92
    private function _getDbInfo($sub_part)
93
    {
94
        list(
95
            $tables,
96
            $num_tables,
97
            $total_num_tables,
98
            ,
99
            $is_show_stats,
100
            $db_is_system_schema,
101
            ,
102
            ,
103
            $pos
104
        ) = Util::getDbInfo($this->db, $sub_part);
105
106
        $this->_tables = $tables;
107
        $this->_num_tables = $num_tables;
108
        $this->_pos = $pos;
109
        $this->_db_is_system_schema = $db_is_system_schema;
110
        $this->_total_num_tables = $total_num_tables;
111
        $this->_is_show_stats = $is_show_stats;
112
    }
113
114
    /**
115
     * Index action
116
     *
117
     * @return void
118
     */
119
    public function indexAction()
120
    {
121
        $response = Response::getInstance();
122
123
        // Add/Remove favorite tables using Ajax request.
124
        if ($response->isAjax() && ! empty($_REQUEST['favorite_table'])) {
125
            $this->addRemoveFavoriteTablesAction();
126
            return;
127
        }
128
129
        // If there is an Ajax request for real row count of a table.
130
        if ($response->isAjax()
131
            && isset($_REQUEST['real_row_count'])
132
            && $_REQUEST['real_row_count'] == true
133
        ) {
134
            $this->handleRealRowCountRequestAction();
135
            return;
136
        }
137
138
        // Drops/deletes/etc. multiple tables if required
139
        if ((! empty($_POST['submit_mult']) && isset($_POST['selected_tbl']))
140
            || isset($_POST['mult_btn'])
141
        ) {
142
            $this->multiSubmitAction();
143
        }
144
145
        $this->response->getHeader()->getScripts()->addFiles(
146
            [
147
                'db_structure.js',
148
                'tbl_change.js',
149
            ]
150
        );
151
152
        // Gets the database structure
153
        $this->_getDbInfo('_structure');
154
155
        // Checks if there are any tables to be shown on current page.
156
        // If there are no tables, the user is redirected to the last page
157
        // having any.
158
        if ($this->_total_num_tables > 0 && $this->_pos > $this->_total_num_tables) {
159
            $uri = './db_structure.php' . Url::getCommonRaw([
160
                'db' => $this->db,
161
                'pos' => max(0, $this->_total_num_tables - $GLOBALS['cfg']['MaxTableList']),
162
                'reload' => 1,
163
            ]);
164
            Core::sendHeaderLocation($uri);
165
        }
166
167
        include_once ROOT_PATH . 'libraries/replication.inc.php';
168
169
        PageSettings::showGroup('DbStructure');
170
171
        // 1. No tables
172
        if ($this->_num_tables == 0) {
173
            $this->response->addHTML(
174
                Message::notice(__('No tables found in database.'))
175
            );
176
            if (empty($this->_db_is_system_schema)) {
177
                $this->response->addHTML(CreateTable::getHtml($this->db));
178
            }
179
            return;
180
        }
181
182
        // else
183
        // 2. Shows table information
184
        /**
185
         * Displays the tables list
186
         */
187
        $this->response->addHTML('<div id="tableslistcontainer">');
188
        $_url_params = [
189
            'pos' => $this->_pos,
190
            'db'  => $this->db,
191
        ];
192
193
        // Add the sort options if they exists
194
        if (isset($_REQUEST['sort'])) {
195
            $_url_params['sort'] = $_REQUEST['sort'];
196
        }
197
198
        if (isset($_REQUEST['sort_order'])) {
199
            $_url_params['sort_order'] = $_REQUEST['sort_order'];
200
        }
201
202
        $this->response->addHTML(
203
            Util::getListNavigator(
204
                $this->_total_num_tables,
205
                $this->_pos,
206
                $_url_params,
207
                'db_structure.php',
208
                'frame_content',
209
                $GLOBALS['cfg']['MaxTableList']
210
            )
211
        );
212
213
        $this->displayTableList();
214
215
        // display again the table list navigator
216
        $this->response->addHTML(
217
            Util::getListNavigator(
218
                $this->_total_num_tables,
219
                $this->_pos,
220
                $_url_params,
221
                'db_structure.php',
222
                'frame_content',
223
                $GLOBALS['cfg']['MaxTableList']
224
            )
225
        );
226
227
        $this->response->addHTML('</div><hr>');
228
229
        /**
230
         * Work on the database
231
         */
232
        /* DATABASE WORK */
233
        /* Printable view of a table */
234
        $this->response->addHTML(
235
            $this->template->render('database/structure/print_view_data_dictionary_link', [
236
                'url_query' => Url::getCommon([
237
                    'db' => $this->db,
238
                    'goto' => 'db_structure.php',
239
                ]),
240
            ])
241
        );
242
243
        if (empty($this->_db_is_system_schema)) {
244
            $this->response->addHTML(CreateTable::getHtml($this->db));
245
        }
246
    }
247
248
    /**
249
     * Add or remove favorite tables
250
     *
251
     * @return void
252
     */
253
    public function addRemoveFavoriteTablesAction()
254
    {
255
        $fav_instance = RecentFavoriteTable::getInstance('favorite');
256
        if (isset($_REQUEST['favorite_tables'])) {
257
            $favorite_tables = json_decode($_REQUEST['favorite_tables'], true);
258
        } else {
259
            $favorite_tables = [];
260
        }
261
        // Required to keep each user's preferences separate.
262
        $user = sha1($GLOBALS['cfg']['Server']['user']);
263
264
        // Request for Synchronization of favorite tables.
265
        if (isset($_REQUEST['sync_favorite_tables'])) {
266
            $cfgRelation = $this->relation->getRelationsParam();
267
            if ($cfgRelation['favoritework']) {
268
                $this->synchronizeFavoriteTables($fav_instance, $user, $favorite_tables);
269
            }
270
            return;
271
        }
272
        $changes = true;
273
        $titles = Util::buildActionTitles();
274
        $favorite_table = $_REQUEST['favorite_table'];
275
        $already_favorite = $this->checkFavoriteTable($favorite_table);
276
277
        if (isset($_REQUEST['remove_favorite'])) {
278
            if ($already_favorite) {
279
                // If already in favorite list, remove it.
280
                $fav_instance->remove($this->db, $favorite_table);
281
                $already_favorite = false; // for favorite_anchor template
282
            }
283
        } elseif (isset($_REQUEST['add_favorite'])) {
284
            if (! $already_favorite) {
285
                $nbTables = count($fav_instance->getTables());
286
                if ($nbTables == $GLOBALS['cfg']['NumFavoriteTables']) {
287
                    $changes = false;
288
                } else {
289
                    // Otherwise add to favorite list.
290
                    $fav_instance->add($this->db, $favorite_table);
291
                    $already_favorite = true;  // for favorite_anchor template
292
                }
293
            }
294
        }
295
296
        $favorite_tables[$user] = $fav_instance->getTables();
297
        $this->response->addJSON('changes', $changes);
298
        if (! $changes) {
299
            $this->response->addJSON(
300
                'message',
301
                $this->template->render('components/error_message', [
302
                    'msg' => __("Favorite List is full!"),
303
                ])
304
            );
305
            return;
306
        }
307
        // Check if current table is already in favorite list.
308
        $favParams = [
309
            'db' => $this->db,
310
            'ajax_request' => true,
311
            'favorite_table' => $favorite_table,
312
            ($already_favorite ? 'remove' : 'add') . '_favorite' => true,
313
        ];
314
        $this->response->addJSON([
315
            'user' => $user,
316
            'favorite_tables' => json_encode($favorite_tables),
317
            'list' => $fav_instance->getHtmlList(),
318
            'anchor' => $this->template->render('database/structure/favorite_anchor', [
319
                'table_name_hash' => md5($favorite_table),
320
                'db_table_name_hash' => md5($this->db . "." . $favorite_table),
321
                'fav_params' => $favParams,
322
                'already_favorite' => $already_favorite,
323
                'titles' => $titles,
324
            ]),
325
        ]);
326
    }
327
328
    /**
329
     * Handles request for real row count on database level view page.
330
     *
331
     * @return void
332
     */
333
    public function handleRealRowCountRequestAction()
334
    {
335
        $ajax_response = $this->response;
336
        // If there is a request to update all table's row count.
337
        if (! isset($_REQUEST['real_row_count_all'])) {
338
            // Get the real row count for the table.
339
            $real_row_count = $this->dbi
340
                ->getTable($this->db, $_REQUEST['table'])
341
                ->getRealRowCountTable();
342
            // Format the number.
343
            $real_row_count = Util::formatNumber($real_row_count, 0);
344
            $ajax_response->addJSON('real_row_count', $real_row_count);
345
            return;
346
        }
347
348
        // Array to store the results.
349
        $real_row_count_all = [];
350
        // Iterate over each table and fetch real row count.
351
        foreach ($this->_tables as $table) {
352
            $row_count = $this->dbi
353
                ->getTable($this->db, $table['TABLE_NAME'])
354
                ->getRealRowCountTable();
355
            $real_row_count_all[] = [
356
                'table' => $table['TABLE_NAME'],
357
                'row_count' => $row_count,
358
            ];
359
        }
360
361
        $ajax_response->addJSON(
362
            'real_row_count_all',
363
            json_encode($real_row_count_all)
364
        );
365
    }
366
367
    /**
368
     * Handles actions related to multiple tables
369
     *
370
     * @return void
371
     */
372
    public function multiSubmitAction()
373
    {
374
        $action = 'db_structure.php';
375
        $err_url = 'db_structure.php' . Url::getCommon(
376
            ['db' => $this->db]
377
        );
378
379
        // see bug #2794840; in this case, code path is:
380
        // db_structure.php -> libraries/mult_submits.inc.php -> sql.php
381
        // -> db_structure.php and if we got an error on the multi submit,
382
        // we must display it here and not call again mult_submits.inc.php
383
        if (! isset($_POST['error']) || false === $_POST['error']) {
384
            include ROOT_PATH . 'libraries/mult_submits.inc.php';
385
        }
386
        if (empty($_POST['message'])) {
387
            $_POST['message'] = Message::success();
388
        }
389
    }
390
391
    /**
392
     * Displays the list of tables
393
     *
394
     * @return void
395
     */
396
    protected function displayTableList()
397
    {
398
        // filtering
399
        $this->response->addHTML(
400
            $this->template->render('filter', ['filter_value' => ''])
401
        );
402
        // table form
403
        $this->response->addHTML(
404
            $this->template->render('database/structure/table_header', [
405
                'db' => $this->db,
406
                'db_is_system_schema' => $this->_db_is_system_schema,
407
                'replication' => $GLOBALS['replication_info']['slave']['status'],
408
                'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
409
                'is_show_stats' => $GLOBALS['is_show_stats'],
410
                'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
411
                'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
412
                'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
413
                'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
414
                'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
415
                'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
416
            ])
417
        );
418
419
        $i = $sum_entries = 0;
420
        $overhead_check = false;
421
        $create_time_all = '';
422
        $update_time_all = '';
423
        $check_time_all = '';
424
        $num_columns = $GLOBALS['cfg']['PropertiesNumColumns'] > 1
425
            ? ceil($this->_num_tables / $GLOBALS['cfg']['PropertiesNumColumns']) + 1
426
            : 0;
427
        $row_count      = 0;
428
        $sum_size       = 0;
429
        $overhead_size  = 0;
430
431
        $hidden_fields = [];
432
        $overall_approx_rows = false;
433
        foreach ($this->_tables as $keyname => $current_table) {
434
            // Get valid statistics whatever is the table type
435
436
            $drop_query = '';
437
            $drop_message = '';
438
            $overhead = '';
439
            $input_class = ['checkall'];
440
441
            $table_is_view = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $table_is_view is dead and can be removed.
Loading history...
442
            // Sets parameters for links
443
            $tbl_url_query = Url::getCommon(
444
                [
445
                    'db' => $this->db,
446
                    'table' => $current_table['TABLE_NAME'],
447
                ]
448
            );
449
            // do not list the previous table's size info for a view
450
451
            list($current_table, $formatted_size, $unit, $formatted_overhead,
452
                $overhead_unit, $overhead_size, $table_is_view, $sum_size)
453
                    = $this->getStuffForEngineTypeTable(
454
                        $current_table,
455
                        $sum_size,
456
                        $overhead_size
457
                    );
458
459
            $curTable = $this->dbi
460
                ->getTable($this->db, $current_table['TABLE_NAME']);
461
            if (! $curTable->isMerge()) {
462
                $sum_entries += $current_table['TABLE_ROWS'];
463
            }
464
465
            if (isset($current_table['Collation'])) {
466
                $collation = '<dfn title="'
467
                    . Charsets::getCollationDescr($current_table['Collation']) . '">'
468
                    . $current_table['Collation'] . '</dfn>';
469
            } else {
470
                $collation = '---';
471
            }
472
473
            if ($this->_is_show_stats) {
474
                if ($formatted_overhead != '') {
475
                    $overhead = '<a href="tbl_structure.php'
476
                        . $tbl_url_query . '#showusage">'
477
                        . '<span>' . $formatted_overhead . '</span>&nbsp;'
478
                        . '<span class="unit">' . $overhead_unit . '</span>'
479
                        . '</a>' . "\n";
480
                    $overhead_check = true;
481
                    $input_class[] = 'tbl-overhead';
482
                } else {
483
                    $overhead = '-';
484
                }
485
            } // end if
486
487
            if ($GLOBALS['cfg']['ShowDbStructureCharset']) {
488
                if (isset($current_table['Collation'])) {
489
                    $charset = mb_substr($collation, 0, mb_strpos($collation, "_"));
490
                } else {
491
                    $charset = '';
492
                }
493
            }
494
495
            if ($GLOBALS['cfg']['ShowDbStructureCreation']) {
496
                $create_time = isset($current_table['Create_time'])
497
                    ? $current_table['Create_time'] : '';
498
                if ($create_time
499
                    && (! $create_time_all
500
                    || $create_time < $create_time_all)
501
                ) {
502
                    $create_time_all = $create_time;
503
                }
504
            }
505
506
            if ($GLOBALS['cfg']['ShowDbStructureLastUpdate']) {
507
                $update_time = isset($current_table['Update_time'])
508
                    ? $current_table['Update_time'] : '';
509
                if ($update_time
510
                    && (! $update_time_all
511
                    || $update_time < $update_time_all)
512
                ) {
513
                    $update_time_all = $update_time;
514
                }
515
            }
516
517
            if ($GLOBALS['cfg']['ShowDbStructureLastCheck']) {
518
                $check_time = isset($current_table['Check_time'])
519
                    ? $current_table['Check_time'] : '';
520
                if ($check_time
521
                    && (! $check_time_all
522
                    || $check_time < $check_time_all)
523
                ) {
524
                    $check_time_all = $check_time;
525
                }
526
            }
527
528
            $truename = ! empty($tooltip_truename)
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $tooltip_truename does not exist. Did you maybe mean $truename?
Loading history...
529
                    && isset($tooltip_truename[$current_table['TABLE_NAME']])
530
                ? $tooltip_truename[$current_table['TABLE_NAME']]
531
                : $current_table['TABLE_NAME'];
532
533
            $i++;
534
535
            $row_count++;
536
            if ($table_is_view) {
537
                $hidden_fields[] = '<input type="hidden" name="views[]" value="'
538
                    . htmlspecialchars($current_table['TABLE_NAME']) . '">';
539
            }
540
541
            /*
542
             * Always activate links for Browse, Search and Empty, even if
543
             * the icons are greyed, because
544
             * 1. for views, we don't know the number of rows at this point
545
             * 2. for tables, another source could have populated them since the
546
             *    page was generated
547
             *
548
             * I could have used the PHP ternary conditional operator but I find
549
             * the code easier to read without this operator.
550
             */
551
            $may_have_rows = $current_table['TABLE_ROWS'] > 0 || $table_is_view;
0 ignored issues
show
introduced by
$table_is_view is of type mixed, thus it always evaluated to true.
Loading history...
552
            $titles = Util::buildActionTitles();
553
554
            if (! $this->_db_is_system_schema) {
555
                $drop_query = sprintf(
556
                    'DROP %s %s',
557
                    $table_is_view || $current_table['ENGINE'] == null ? 'VIEW'
558
                    : 'TABLE',
559
                    Util::backquote(
0 ignored issues
show
Bug introduced by
It seems like PhpMyAdmin\Util::backquo...nt_table['TABLE_NAME']) can also be of type array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

559
                    /** @scrutinizer ignore-type */ Util::backquote(
Loading history...
560
                        $current_table['TABLE_NAME']
561
                    )
562
                );
563
                $drop_message = sprintf(
564
                    ($table_is_view || $current_table['ENGINE'] == null
565
                        ? __('View %s has been dropped.')
566
                        : __('Table %s has been dropped.')),
567
                    str_replace(
568
                        ' ',
569
                        '&nbsp;',
570
                        htmlspecialchars($current_table['TABLE_NAME'])
571
                    )
572
                );
573
            }
574
575
            if ($num_columns > 0
576
                && $this->_num_tables > $num_columns
577
                && ($row_count % $num_columns) == 0
578
            ) {
579
                $row_count = 1;
580
581
                $this->response->addHTML(
582
                    '</tr></tbody></table></div></form>'
583
                );
584
585
                $this->response->addHTML(
586
                    $this->template->render('database/structure/table_header', [
587
                        'db' => $this->db,
588
                        'db_is_system_schema' => $this->_db_is_system_schema,
589
                        'replication' => $GLOBALS['replication_info']['slave']['status'],
590
                        'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
591
                        'is_show_stats' => $GLOBALS['is_show_stats'],
592
                        'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
593
                        'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
594
                        'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
595
                        'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
596
                        'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
597
                        'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
598
                    ])
599
                );
600
            }
601
602
            list($approx_rows, $show_superscript) = $this->isRowCountApproximated(
603
                $current_table,
604
                $table_is_view
605
            );
606
607
            list($do, $ignored) = $this->getReplicationStatus($truename);
608
609
            $this->response->addHTML(
610
                $this->template->render('database/structure/structure_table_row', [
611
                    'table_name_hash' => md5($current_table['TABLE_NAME']),
612
                    'db_table_name_hash' => md5($this->db . '.' . $current_table['TABLE_NAME']),
613
                    'db' => $this->db,
614
                    'curr' => $i,
615
                    'input_class' => implode(' ', $input_class),
616
                    'table_is_view' => $table_is_view,
617
                    'current_table' => $current_table,
618
                    'browse_table_title' => $may_have_rows ? $titles['Browse'] : $titles['NoBrowse'],
619
                    'search_table_title' => $may_have_rows ? $titles['Search'] : $titles['NoSearch'],
620
                    'browse_table_label_title' => htmlspecialchars($current_table['TABLE_COMMENT']),
621
                    'browse_table_label_truename' => $truename,
622
                    'empty_table_sql_query' => urlencode(
623
                        'TRUNCATE ' . Util::backquote(
0 ignored issues
show
Bug introduced by
Are you sure PhpMyAdmin\Util::backquo...nt_table['TABLE_NAME']) of type array|mixed|string can be used in concatenation? ( Ignorable by Annotation )

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

623
                        'TRUNCATE ' . /** @scrutinizer ignore-type */ Util::backquote(
Loading history...
624
                            $current_table['TABLE_NAME']
625
                        )
626
                    ),
627
                    'empty_table_message_to_show' => urlencode(
628
                        sprintf(
629
                            __('Table %s has been emptied.'),
630
                            htmlspecialchars(
631
                                $current_table['TABLE_NAME']
632
                            )
633
                        )
634
                    ),
635
                    'empty_table_title' => $may_have_rows ? $titles['Empty'] : $titles['NoEmpty'],
636
                    'tracking_icon' => $this->getTrackingIcon($truename),
637
                    'server_slave_status' => $GLOBALS['replication_info']['slave']['status'],
638
                    'tbl_url_query' => $tbl_url_query,
639
                    'db_is_system_schema' => $this->_db_is_system_schema,
640
                    'titles' => $titles,
641
                    'drop_query' => $drop_query,
642
                    'drop_message' => $drop_message,
643
                    'collation' => $collation,
644
                    'formatted_size' => $formatted_size,
645
                    'unit' => $unit,
646
                    'overhead' => $overhead,
647
                    'create_time' => isset($create_time) && $create_time
648
                        ? Util::localisedDate(strtotime($create_time)) : '-',
649
                    'update_time' => isset($update_time) && $update_time
650
                        ? Util::localisedDate(strtotime($update_time)) : '-',
651
                    'check_time' => isset($check_time) && $check_time
652
                        ? Util::localisedDate(strtotime($check_time)) : '-',
653
                    'charset' => isset($charset)
654
                        ? $charset : '',
655
                    'is_show_stats' => $this->_is_show_stats,
656
                    'ignored' => $ignored,
657
                    'do' => $do,
658
                    'approx_rows' => $approx_rows,
659
                    'show_superscript' => $show_superscript,
660
                    'already_favorite' => $this->checkFavoriteTable(
661
                        $current_table['TABLE_NAME']
662
                    ),
663
                    'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
664
                    'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
665
                    'limit_chars' => $GLOBALS['cfg']['LimitChars'],
666
                    'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
667
                    'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
668
                    'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
669
                    'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
670
                    'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
671
                ])
672
            );
673
674
            $overall_approx_rows = $overall_approx_rows || $approx_rows;
675
        } // end foreach
676
677
        $this->response->addHTML('</tbody>');
678
679
        $db_collation = $this->dbi->getDbCollation($this->db);
680
        $db_charset = mb_substr($db_collation, 0, mb_strpos($db_collation, "_"));
681
682
        // Show Summary
683
        $this->response->addHTML(
684
            $this->template->render('database/structure/body_for_table_summary', [
685
                'num_tables' => $this->_num_tables,
686
                'server_slave_status' => $GLOBALS['replication_info']['slave']['status'],
687
                'db_is_system_schema' => $this->_db_is_system_schema,
688
                'sum_entries' => $sum_entries,
689
                'db_collation' => $db_collation,
690
                'is_show_stats' => $this->_is_show_stats,
691
                'db_charset' => $db_charset,
692
                'sum_size' => $sum_size,
693
                'overhead_size' => $overhead_size,
694
                'create_time_all' => $create_time_all ? Util::localisedDate(strtotime($create_time_all)) : '-',
695
                'update_time_all' => $update_time_all ? Util::localisedDate(strtotime($update_time_all)) : '-',
696
                'check_time_all' => $check_time_all ? Util::localisedDate(strtotime($check_time_all)) : '-',
697
                'approx_rows' => $overall_approx_rows,
698
                'num_favorite_tables' => $GLOBALS['cfg']['NumFavoriteTables'],
699
                'db' => $GLOBALS['db'],
700
                'properties_num_columns' => $GLOBALS['cfg']['PropertiesNumColumns'],
701
                'dbi' => $this->dbi,
702
                'show_charset' => $GLOBALS['cfg']['ShowDbStructureCharset'],
703
                'show_comment' => $GLOBALS['cfg']['ShowDbStructureComment'],
704
                'show_creation' => $GLOBALS['cfg']['ShowDbStructureCreation'],
705
                'show_last_update' => $GLOBALS['cfg']['ShowDbStructureLastUpdate'],
706
                'show_last_check' => $GLOBALS['cfg']['ShowDbStructureLastCheck'],
707
            ])
708
        );
709
        $this->response->addHTML('</table>');
710
711
        //check all
712
        $this->response->addHTML(
713
            $this->template->render('database/structure/check_all_tables', [
714
                'pma_theme_image' => $GLOBALS['pmaThemeImage'],
715
                'text_dir' => $GLOBALS['text_dir'],
716
                'overhead_check' => $overhead_check,
717
                'db_is_system_schema' => $this->_db_is_system_schema,
718
                'hidden_fields' => $hidden_fields,
719
                'disable_multi_table' => $GLOBALS['cfg']['DisableMultiTableMaintenance'],
720
                'central_columns_work' => $GLOBALS['cfgRelation']['centralcolumnswork'],
721
            ])
722
        );
723
        $this->response->addHTML('</form>'); //end of form
724
    }
725
726
    /**
727
     * Returns the tracking icon if the table is tracked
728
     *
729
     * @param string $table table name
730
     *
731
     * @return string HTML for tracking icon
732
     */
733
    protected function getTrackingIcon($table)
734
    {
735
        $tracking_icon = '';
736
        if (Tracker::isActive()) {
737
            $is_tracked = Tracker::isTracked($this->db, $table);
738
            if ($is_tracked
739
                || Tracker::getVersion($this->db, $table) > 0
740
            ) {
741
                $tracking_icon = $this->template->render('database/structure/tracking_icon', [
742
                    'db' => $this->db,
743
                    'table' => $table,
744
                    'is_tracked' => $is_tracked,
745
                ]);
746
            }
747
        }
748
        return $tracking_icon;
749
    }
750
751
    /**
752
     * Returns whether the row count is approximated
753
     *
754
     * @param array   $current_table array containing details about the table
755
     * @param boolean $table_is_view whether the table is a view
756
     *
757
     * @return array
758
     */
759
    protected function isRowCountApproximated(array $current_table, $table_is_view)
760
    {
761
        $approx_rows = false;
762
        $show_superscript = '';
763
764
        // there is a null value in the ENGINE
765
        // - when the table needs to be repaired, or
766
        // - when it's a view
767
        //  so ensure that we'll display "in use" below for a table
768
        //  that needs to be repaired
769
        if (isset($current_table['TABLE_ROWS'])
770
            && ($current_table['ENGINE'] != null || $table_is_view)
771
        ) {
772
            // InnoDB/TokuDB table: we did not get an accurate row count
773
            $approx_rows = ! $table_is_view
774
                && in_array($current_table['ENGINE'], ['InnoDB', 'TokuDB'])
775
                && ! $current_table['COUNTED'];
776
777
            if ($table_is_view
778
                && $current_table['TABLE_ROWS'] >= $GLOBALS['cfg']['MaxExactCountViews']
779
            ) {
780
                $approx_rows = true;
781
                $show_superscript = Util::showHint(
782
                    Sanitize::sanitize(
783
                        sprintf(
784
                            __(
785
                                'This view has at least this number of '
786
                                . 'rows. Please refer to %sdocumentation%s.'
787
                            ),
788
                            '[doc@cfg_MaxExactCountViews]',
789
                            '[/doc]'
790
                        )
791
                    )
792
                );
793
            }
794
        }
795
796
        return [
797
            $approx_rows,
798
            $show_superscript,
799
        ];
800
    }
801
802
    /**
803
     * Returns the replication status of the table.
804
     *
805
     * @param string $table table name
806
     *
807
     * @return array
808
     */
809
    protected function getReplicationStatus($table)
810
    {
811
        $do = $ignored = false;
812
        if ($GLOBALS['replication_info']['slave']['status']) {
813
            $nbServSlaveDoDb = count(
814
                $GLOBALS['replication_info']['slave']['Do_DB']
815
            );
816
            $nbServSlaveIgnoreDb = count(
817
                $GLOBALS['replication_info']['slave']['Ignore_DB']
818
            );
819
            $searchDoDBInTruename = array_search(
820
                $table,
821
                $GLOBALS['replication_info']['slave']['Do_DB']
822
            );
823
            $searchDoDBInDB = array_search(
824
                $this->db,
825
                $GLOBALS['replication_info']['slave']['Do_DB']
826
            );
827
828
            $do = strlen($searchDoDBInTruename) > 0
0 ignored issues
show
Bug introduced by
It seems like $searchDoDBInTruename can also be of type false; however, parameter $string of strlen() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

828
            $do = strlen(/** @scrutinizer ignore-type */ $searchDoDBInTruename) > 0
Loading history...
829
                || strlen($searchDoDBInDB) > 0
830
                || ($nbServSlaveDoDb == 0 && $nbServSlaveIgnoreDb == 0)
831
                || $this->hasTable(
832
                    $GLOBALS['replication_info']['slave']['Wild_Do_Table'],
833
                    $table
834
                );
835
836
            $searchDb = array_search(
837
                $this->db,
838
                $GLOBALS['replication_info']['slave']['Ignore_DB']
839
            );
840
            $searchTable = array_search(
841
                $table,
842
                $GLOBALS['replication_info']['slave']['Ignore_Table']
843
            );
844
            $ignored = strlen($searchTable) > 0
845
                || strlen($searchDb) > 0
846
                || $this->hasTable(
847
                    $GLOBALS['replication_info']['slave']['Wild_Ignore_Table'],
848
                    $table
849
                );
850
        }
851
852
        return [
853
            $do,
854
            $ignored,
855
        ];
856
    }
857
858
    /**
859
     * Synchronize favorite tables
860
     *
861
     *
862
     * @param RecentFavoriteTable $fav_instance    Instance of this class
863
     * @param string              $user            The user hash
864
     * @param array               $favorite_tables Existing favorites
865
     *
866
     * @return void
867
     */
868
    protected function synchronizeFavoriteTables(
869
        $fav_instance,
870
        $user,
871
        array $favorite_tables
872
    ) {
873
        $fav_instance_tables = $fav_instance->getTables();
874
875
        if (empty($fav_instance_tables)
876
            && isset($favorite_tables[$user])
877
        ) {
878
            foreach ($favorite_tables[$user] as $key => $value) {
879
                $fav_instance->add($value['db'], $value['table']);
880
            }
881
        }
882
        $favorite_tables[$user] = $fav_instance->getTables();
883
884
        $this->response->addJSON(
885
            [
886
                'favorite_tables' => json_encode($favorite_tables),
887
                'list' => $fav_instance->getHtmlList()
888
            ]
889
        );
890
        $server_id = $GLOBALS['server'];
891
        // Set flag when localStorage and pmadb(if present) are in sync.
892
        $_SESSION['tmpval']['favorites_synced'][$server_id] = true;
893
    }
894
895
    /**
896
     * Function to check if a table is already in favorite list.
897
     *
898
     * @param string $current_table current table
899
     *
900
     * @return true|false
901
     */
902
    protected function checkFavoriteTable($current_table)
903
    {
904
        // ensure $_SESSION['tmpval']['favorite_tables'] is initialized
905
        RecentFavoriteTable::getInstance('favorite');
906
        foreach ($_SESSION['tmpval']['favorite_tables'][$GLOBALS['server']] as $value) {
907
            if ($value['db'] == $this->db && $value['table'] == $current_table) {
908
                return true;
909
            }
910
        }
911
        return false;
912
    }
913
914
    /**
915
     * Find table with truename
916
     *
917
     * @param array  $db       DB to look into
918
     * @param string $truename Table name
919
     *
920
     * @return bool
921
     */
922
    protected function hasTable(array $db, $truename)
923
    {
924
        foreach ($db as $db_table) {
925
            if ($this->db == $this->replication->extractDbOrTable($db_table)
926
                && preg_match(
927
                    '@^' .
928
                    preg_quote(mb_substr($this->replication->extractDbOrTable($db_table, 'table'), 0, -1), '@') . '@',
929
                    $truename
930
                )
931
            ) {
932
                return true;
933
            }
934
        }
935
        return false;
936
    }
937
938
    /**
939
     * Get the value set for ENGINE table,
940
     *
941
     * @param array   $current_table current table
942
     * @param integer $sum_size      total table size
943
     * @param integer $overhead_size overhead size
944
     *
945
     * @return array
946
     * @internal param bool $table_is_view whether table is view or not
947
     */
948
    protected function getStuffForEngineTypeTable(
949
        array $current_table,
950
        $sum_size,
951
        $overhead_size
952
    ) {
953
        $formatted_size = '-';
954
        $unit = '';
955
        $formatted_overhead = '';
956
        $overhead_unit = '';
957
        $table_is_view = false;
958
959
        switch ($current_table['ENGINE']) {
960
        // MyISAM, ISAM or Heap table: Row count, data size and index size
961
        // are accurate; data size is accurate for ARCHIVE
962
            case 'MyISAM':
963
            case 'ISAM':
964
            case 'HEAP':
965
            case 'MEMORY':
966
            case 'ARCHIVE':
967
            case 'Aria':
968
            case 'Maria':
969
                list($current_table, $formatted_size, $unit, $formatted_overhead,
970
                $overhead_unit, $overhead_size, $sum_size)
971
                    = $this->getValuesForAriaTable(
972
                        $current_table,
973
                        $sum_size,
974
                        $overhead_size,
975
                        $formatted_size,
0 ignored issues
show
Bug introduced by
$formatted_size of type string is incompatible with the type integer expected by parameter $formatted_size of PhpMyAdmin\Controllers\D...getValuesForAriaTable(). ( Ignorable by Annotation )

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

975
                        /** @scrutinizer ignore-type */ $formatted_size,
Loading history...
976
                        $unit,
977
                        $formatted_overhead,
0 ignored issues
show
Bug introduced by
$formatted_overhead of type string is incompatible with the type integer expected by parameter $formatted_overhead of PhpMyAdmin\Controllers\D...getValuesForAriaTable(). ( Ignorable by Annotation )

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

977
                        /** @scrutinizer ignore-type */ $formatted_overhead,
Loading history...
978
                        $overhead_unit
979
                    );
980
                break;
981
            case 'InnoDB':
982
            case 'PBMS':
983
            case 'TokuDB':
984
                // InnoDB table: Row count is not accurate but data and index sizes are.
985
                // PBMS table in Drizzle: TABLE_ROWS is taken from table cache,
986
                // so it may be unavailable
987
                list($current_table, $formatted_size, $unit, $sum_size)
988
                = $this->getValuesForInnodbTable(
989
                    $current_table,
990
                    $sum_size
991
                );
992
                break;
993
        // Mysql 5.0.x (and lower) uses MRG_MyISAM
994
        // and MySQL 5.1.x (and higher) uses MRG_MYISAM
995
        // Both are aliases for MERGE
996
            case 'MRG_MyISAM':
997
            case 'MRG_MYISAM':
998
            case 'MERGE':
999
            case 'BerkeleyDB':
1000
                // Merge or BerkleyDB table: Only row count is accurate.
1001
                if ($this->_is_show_stats) {
1002
                    $formatted_size =  ' - ';
1003
                    $unit          =  '';
1004
                }
1005
                break;
1006
        // for a view, the ENGINE is sometimes reported as null,
1007
        // or on some servers it's reported as "SYSTEM VIEW"
1008
            case null:
1009
            case 'SYSTEM VIEW':
1010
                // possibly a view, do nothing
1011
                break;
1012
            default:
1013
                // Unknown table type.
1014
                if ($this->_is_show_stats) {
1015
                    $formatted_size =  __('unknown');
1016
                    $unit          =  '';
1017
                }
1018
        } // end switch
1019
1020
        if ($current_table['TABLE_TYPE'] == 'VIEW'
1021
            || $current_table['TABLE_TYPE'] == 'SYSTEM VIEW'
1022
        ) {
1023
            // countRecords() takes care of $cfg['MaxExactCountViews']
1024
            $current_table['TABLE_ROWS'] = $this->dbi
1025
                ->getTable($this->db, $current_table['TABLE_NAME'])
1026
                ->countRecords(true);
1027
            $table_is_view = true;
1028
        }
1029
1030
        return [
1031
            $current_table,
1032
            $formatted_size,
1033
            $unit,
1034
            $formatted_overhead,
1035
            $overhead_unit,
1036
            $overhead_size,
1037
            $table_is_view,
1038
            $sum_size,
1039
        ];
1040
    }
1041
1042
    /**
1043
     * Get values for ARIA/MARIA tables
1044
     *
1045
     * @param array   $current_table      current table
1046
     * @param integer $sum_size           sum size
1047
     * @param integer $overhead_size      overhead size
1048
     * @param integer $formatted_size     formatted size
1049
     * @param string  $unit               unit
1050
     * @param integer $formatted_overhead overhead formatted
1051
     * @param string  $overhead_unit      overhead unit
1052
     *
1053
     * @return array
1054
     */
1055
    protected function getValuesForAriaTable(
1056
        array $current_table,
1057
        $sum_size,
1058
        $overhead_size,
1059
        $formatted_size,
1060
        $unit,
1061
        $formatted_overhead,
1062
        $overhead_unit
1063
    ) {
1064
        if ($this->_db_is_system_schema) {
1065
            $current_table['Rows'] = $this->dbi
1066
                ->getTable($this->db, $current_table['Name'])
1067
                ->countRecords();
1068
        }
1069
1070
        if ($this->_is_show_stats) {
1071
            $tblsize = $current_table['Data_length']
1072
                + $current_table['Index_length'];
1073
            $sum_size += $tblsize;
1074
            list($formatted_size, $unit) = Util::formatByteDown(
1075
                $tblsize,
1076
                3,
1077
                $tblsize > 0 ? 1 : 0
1078
            );
1079
            if (isset($current_table['Data_free'])
1080
                && $current_table['Data_free'] > 0
1081
            ) {
1082
                list($formatted_overhead, $overhead_unit)
1083
                    = Util::formatByteDown(
1084
                        $current_table['Data_free'],
1085
                        3,
1086
                        ($current_table['Data_free'] > 0 ? 1 : 0)
1087
                    );
1088
                $overhead_size += $current_table['Data_free'];
1089
            }
1090
        }
1091
        return [
1092
            $current_table,
1093
            $formatted_size,
1094
            $unit,
1095
            $formatted_overhead,
1096
            $overhead_unit,
1097
            $overhead_size,
1098
            $sum_size,
1099
        ];
1100
    }
1101
1102
    /**
1103
     * Get values for InnoDB table
1104
     *
1105
     * @param array   $current_table current table
1106
     * @param integer $sum_size      sum size
1107
     *
1108
     * @return array
1109
     */
1110
    protected function getValuesForInnodbTable(
1111
        array $current_table,
1112
        $sum_size
1113
    ) {
1114
        $formatted_size = $unit = '';
1115
1116
        if ((in_array($current_table['ENGINE'], ['InnoDB', 'TokuDB'])
1117
            && $current_table['TABLE_ROWS'] < $GLOBALS['cfg']['MaxExactCount'])
1118
            || ! isset($current_table['TABLE_ROWS'])
1119
        ) {
1120
            $current_table['COUNTED'] = true;
1121
            $current_table['TABLE_ROWS'] = $this->dbi
1122
                ->getTable($this->db, $current_table['TABLE_NAME'])
1123
                ->countRecords(true);
1124
        } else {
1125
            $current_table['COUNTED'] = false;
1126
        }
1127
1128
        if ($this->_is_show_stats) {
1129
            $tblsize = $current_table['Data_length']
1130
                + $current_table['Index_length'];
1131
            $sum_size += $tblsize;
1132
            list($formatted_size, $unit) = Util::formatByteDown(
1133
                $tblsize,
1134
                3,
1135
                ($tblsize > 0 ? 1 : 0)
1136
            );
1137
        }
1138
1139
        return [
1140
            $current_table,
1141
            $formatted_size,
1142
            $unit,
1143
            $sum_size,
1144
        ];
1145
    }
1146
}
1147