Test Failed
Branch develop (db5506)
by Felipe
03:46
created

DisplayController::doEditRow()   F

Complexity

Conditions 32

Size

Total Lines 184
Code Lines 117

Duplication

Lines 17
Ratio 9.24 %

Importance

Changes 0
Metric Value
cc 32
eloc 117
nop 2
dl 17
loc 184
rs 2
c 0
b 0
f 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
3
namespace PHPPgAdmin\Controller;
4
5
/**
6
 * Base controller class
7
 */
8
class DisplayController extends BaseController
9
{
10
    public $_name = 'DisplayController';
11
12
    public function render()
0 ignored issues
show
Coding Style introduced by
render uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
render uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
13
    {
14
        $conf           = $this->conf;
15
        $misc           = $this->misc;
16
        $lang           = $this->lang;
17
        $plugin_manager = $this->plugin_manager;
18
        $data           = $misc->getDatabaseAccessor();
19
        $action         = $this->action;
20
21
        /* shortcuts: this function exit the script for ajax purpose */
22
        if ($action == 'dobrowsefk') {
23
            $this->doBrowseFK();
24
        }
25
26
        set_time_limit(0);
27
28
        $scripts = '<script src="' . SUBFOLDER . '/js/display.js" type="text/javascript"></script>';
29
30
        $scripts .= '<script type="text/javascript">' . "\n";
31
        $scripts .= "var Display = {\n";
32
        $scripts .= "errmsg: '" . str_replace("'", "\'", $lang['strconnectionfail']) . "'\n";
33
        $scripts .= "};\n";
34
        $scripts .= '</script>' . "\n";
35
36
        $footer_template = 'footer.twig';
37
        $header_template = 'header.twig';
38
39
        ob_start();
40
        switch ($action) {
41 View Code Duplication
            case 'editrow':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
42
                $header_template = 'header_sqledit.twig';
43
                $footer_template = 'footer_sqledit.twig';
44
                if (isset($_POST['save'])) {
45
                    $this->doEditRow(false);
46
                } else {
47
                    $this->doBrowse();
48
                }
49
50
                break;
51
            case 'confeditrow':
52
                $this->doEditRow(true);
53
                break;
54 View Code Duplication
            case 'delrow':
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
55
                $header_template = 'header_sqledit.twig';
56
                $footer_template = 'footer_sqledit.twig';
57
                if (isset($_POST['yes'])) {
58
                    $this->doDelRow(false);
59
                } else {
60
                    $this->doBrowse();
61
                }
62
63
                break;
64
            case 'confdelrow':
65
                $this->doDelRow(true);
66
                break;
67
            default:
68
                $header_template = 'header_sqledit.twig';
69
                $footer_template = 'footer_sqledit.twig';
70
                $this->doBrowse();
71
                break;
72
        }
73
        $output = ob_get_clean();
74
75
        // Set the title based on the subject of the request
76
        if (isset($_REQUEST['subject']) && isset($_REQUEST[$_REQUEST['subject']])) {
77
            if ($_REQUEST['subject'] == 'table') {
78
                $this->printHeader($lang['strtables'] . ': ' . $_REQUEST[$_REQUEST['subject']], $scripts, true, $header_template);
79 View Code Duplication
            } elseif ($_REQUEST['subject'] == 'view') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
80
                $this->printHeader($lang['strviews'] . ': ' . $_REQUEST[$_REQUEST['subject']], $scripts, true, $header_template);
81
            } elseif ($_REQUEST['subject'] == 'matview') {
82
                $this->printHeader('M' . $lang['strviews'] . ': ' . $_REQUEST[$_REQUEST['subject']], $scripts, true, $header_template);
83 View Code Duplication
            } elseif ($_REQUEST['subject'] == 'column') {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
84
                $this->printHeader($lang['strcolumn'] . ': ' . $_REQUEST[$_REQUEST['subject']], $scripts, true, $header_template);
85
            }
86
        } else {
87
            $this->printHeader($lang['strqueryresults'], $scripts, true, $header_template);
88
        }
89
90
        $this->printBody();
91
92
        echo $output;
93
94
        $this->printFooter(true, $footer_template);
95
    }
96
97
    /**
98
     * Displays requested data
99
     */
100
    public function doBrowse($msg = '')
0 ignored issues
show
Coding Style introduced by
doBrowse uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doBrowse uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doBrowse uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
101
    {
102
        $conf           = $this->conf;
103
        $misc           = $this->misc;
104
        $lang           = $this->lang;
105
        $plugin_manager = $this->plugin_manager;
106
        $data           = $misc->getDatabaseAccessor();
107
108
        $save_history = false;
109
        // If current page is not set, default to first page
110
        if (!isset($_REQUEST['page'])) {
111
            $_REQUEST['page'] = 1;
112
        }
113
114
        if (!isset($_REQUEST['nohistory'])) {
115
            $save_history = true;
116
        }
117
118
        if (isset($_REQUEST['subject'])) {
119
            $subject = $_REQUEST['subject'];
120
            if (isset($_REQUEST[$subject])) {
121
                $object = $_REQUEST[$subject];
122
            }
123
        } else {
124
            $subject = '';
125
        }
126
127
        $this->printTrail(isset($subject) ? $subject : 'database');
128
        $this->printTabs($subject, 'browse');
129
130
        /* This code is used when browsing FK in pure-xHTML (without js) */
131
        if (isset($_REQUEST['fkey'])) {
132
            $ops = [];
133
            foreach ($_REQUEST['fkey'] as $x => $y) {
134
                $ops[$x] = '=';
135
            }
136
            $query             = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
137
            $_REQUEST['query'] = $query;
138
        }
139
140
        if (isset($object)) {
141
            if (isset($_REQUEST['query'])) {
142
                $_SESSION['sqlquery'] = $_REQUEST['query'];
143
                $this->printTitle($lang['strselect']);
144
                $type = 'SELECT';
145
            } else {
146
                $type = 'TABLE';
147
            }
148
        } else {
149
            $this->printTitle($lang['strqueryresults']);
150
            /*we comes from sql.php, $_SESSION['sqlquery'] has been set there */
151
            $type = 'QUERY';
152
        }
153
154
        $this->printMsg($msg);
155
156
        // If 'sortkey' is not set, default to ''
157
        if (!isset($_REQUEST['sortkey'])) {
158
            $_REQUEST['sortkey'] = '';
159
        }
160
161
        // If 'sortdir' is not set, default to ''
162
        if (!isset($_REQUEST['sortdir'])) {
163
            $_REQUEST['sortdir'] = '';
164
        }
165
166
        // If 'strings' is not set, default to collapsed
167
        if (!isset($_REQUEST['strings'])) {
168
            $_REQUEST['strings'] = 'collapsed';
169
        }
170
171
        // Fetch unique row identifier, if this is a table browse request.
172
        if (isset($object)) {
173
            $key = $data->getRowIdentifier($object);
174
        } else {
175
            $key = [];
176
        }
177
178
        // Set the schema search path
179 View Code Duplication
        if (isset($_REQUEST['search_path'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
180
            if ($data->setSearchPath(array_map('trim', explode(',', $_REQUEST['search_path']))) != 0) {
181
                return;
182
            }
183
        }
184
185
        try {
186
            // Retrieve page from query.  $max_pages is returned by reference.
187
            $rs = $data->browseQuery($type,
188
                isset($object) ? $object : null,
189
                isset($_SESSION['sqlquery']) ? $_SESSION['sqlquery'] : null,
190
                $_REQUEST['sortkey'], $_REQUEST['sortdir'], $_REQUEST['page'],
191
                $conf['max_rows'], $max_pages);
192
        } catch (\PHPPgAdmin\ADOdbException $e) {
193
            return;
194
        }
195
196
        $fkey_information = $this->getFKInfo();
197
198
        // Build strings for GETs in array
199
        $_gets = [
200
            'server'   => $_REQUEST['server'],
201
            'database' => $_REQUEST['database'],
202
        ];
203
204
        if (isset($_REQUEST['schema'])) {
205
            $_gets['schema'] = $_REQUEST['schema'];
206
        }
207
208
        if (isset($object)) {
209
            $_gets[$subject] = $object;
210
        }
211
212
        if (isset($subject)) {
213
            $_gets['subject'] = $subject;
214
        }
215
216
        if (isset($_REQUEST['query'])) {
217
            $_gets['query'] = $_REQUEST['query'];
218
        }
219
220
        if (isset($_REQUEST['count'])) {
221
            $_gets['count'] = $_REQUEST['count'];
222
        }
223
224
        if (isset($_REQUEST['return'])) {
225
            $_gets['return'] = $_REQUEST['return'];
226
        }
227
228
        if (isset($_REQUEST['search_path'])) {
229
            $_gets['search_path'] = $_REQUEST['search_path'];
230
        }
231
232
        if (isset($_REQUEST['table'])) {
233
            $_gets['table'] = $_REQUEST['table'];
234
        }
235
236
        if (isset($_REQUEST['sortkey'])) {
237
            $_gets['sortkey'] = $_REQUEST['sortkey'];
238
        }
239
240
        if (isset($_REQUEST['sortdir'])) {
241
            $_gets['sortdir'] = $_REQUEST['sortdir'];
242
        }
243
244
        if (isset($_REQUEST['nohistory'])) {
245
            $_gets['nohistory'] = $_REQUEST['nohistory'];
246
        }
247
248
        $_gets['strings'] = $_REQUEST['strings'];
249
250
        if ($save_history && is_object($rs) && ($type == 'QUERY')) { //{
251
            $misc->saveScriptHistory($_REQUEST['query']);
252
        }
253
254
        if (isset($_REQUEST['query'])) {
255
            $query = $_REQUEST['query'];
256
        } else {
257
            $query = "SELECT * FROM {$_REQUEST['schema']}";
258
            if ($_REQUEST['subject'] == 'matview') {
259
                $query = "{$query}.{$_REQUEST['matview']};";
260
            } elseif ($_REQUEST['subject'] == 'view') {
261
                $query = "{$query}.{$_REQUEST['view']};";
262
            } else {
263
                $query = "{$query}.{$_REQUEST['table']};";
264
            }
265
        }
266
        //$query = isset($_REQUEST['query'])? $_REQUEST['query'] : "select * from {$_REQUEST['schema']}.{$_REQUEST['table']};";
267
        $this->prtrace($query);
268
        //die(htmlspecialchars($query));
269
270
        echo '<form method="post" id="sqlform" action="' . $_SERVER['REQUEST_URI'] . '">';
0 ignored issues
show
Security introduced by
'<form method="post" id=...R['REQUEST_URI'] . '">' can contain request data and is used in output context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read tainted data from array
    in src/controllers/DisplayController.php on line 270

Preventing Cross-Site-Scripting Attacks

Cross-Site-Scripting allows an attacker to inject malicious code into your website - in particular Javascript code, and have that code executed with the privileges of a visiting user. This can be used to obtain data, or perform actions on behalf of that visiting user.

In order to prevent this, make sure to escape all user-provided data:

// for HTML
$sanitized = htmlentities($tainted, ENT_QUOTES);

// for URLs
$sanitized = urlencode($tainted);

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
271
        echo '<textarea width="90%" name="query"  id="query" rows="5" cols="100" resizable="true">';
272
273
        echo htmlspecialchars($query);
274
        echo '</textarea><br><input type="submit"/></form>';
275
276
        if (is_object($rs) && $rs->recordCount() > 0) {
277
            // Show page navigation
278
            $misc->printPages($_REQUEST['page'], $max_pages, $_gets);
279
280
            echo "<table id=\"data\">\n<tr>";
281
282
            // Check that the key is actually in the result set.  This can occur for select
283
            // operations where the key fields aren't part of the select.  XXX:  We should
284
            // be able to support this, somehow.
285
            foreach ($key as $v) {
286
                // If a key column is not found in the record set, then we
287
                // can't use the key.
288
                if (!in_array($v, array_keys($rs->fields))) {
289
                    $key = [];
290
                    break;
291
                }
292
            }
293
294
            $buttons = [
295
                'edit'   => [
296
                    'content' => $lang['stredit'],
297
                    'attr'    => [
298
                        'href' => [
299
                            'url'     => 'display.php',
300
                            'urlvars' => array_merge([
301
                                'action'  => 'confeditrow',
302
                                'strings' => $_REQUEST['strings'],
303
                                'page'    => $_REQUEST['page'],
304
                            ], $_gets),
305
                        ],
306
                    ],
307
                ],
308
                'delete' => [
309
                    'content' => $lang['strdelete'],
310
                    'attr'    => [
311
                        'href' => [
312
                            'url'     => 'display.php',
313
                            'urlvars' => array_merge([
314
                                'action'  => 'confdelrow',
315
                                'strings' => $_REQUEST['strings'],
316
                                'page'    => $_REQUEST['page'],
317
                            ], $_gets),
318
                        ],
319
                    ],
320
                ],
321
            ];
322
            $actions = [
323
                'actionbuttons' => &$buttons,
324
                'place'         => 'display-browse',
325
            ];
326
            $plugin_manager->do_hook('actionbuttons', $actions);
327
328
            foreach (array_keys($actions['actionbuttons']) as $action) {
329
                $actions['actionbuttons'][$action]['attr']['href']['urlvars'] = array_merge(
330
                    $actions['actionbuttons'][$action]['attr']['href']['urlvars'],
331
                    $_gets
332
                );
333
            }
334
335
            $edit_params = isset($actions['actionbuttons']['edit']) ?
336
            $actions['actionbuttons']['edit'] : [];
337
            $delete_params = isset($actions['actionbuttons']['delete']) ?
338
            $actions['actionbuttons']['delete'] : [];
339
340
            // Display edit and delete actions if we have a key
341
            $colspan = count($buttons);
342
            if ($colspan > 0 and count($key) > 0) {
343
                echo "<th colspan=\"{$colspan}\" class=\"data\">{$lang['stractions']}</th>" . "\n";
344
            }
345
346
            /* we show OIDs only if we are in TABLE or SELECT type browsing */
347
            $this->printTableHeaderCells($rs, $_gets, isset($object));
348
349
            echo '</tr>' . "\n";
350
351
            $i = 0;
352
            reset($rs->fields);
353
            while (!$rs->EOF) {
354
                $id = (($i % 2) == 0 ? '1' : '2');
355
                echo "<tr class=\"data{$id}\">" . "\n";
356
                // Display edit and delete links if we have a key
357
                if ($colspan > 0 and count($key) > 0) {
358
                    $keys_array = [];
359
                    $has_nulls  = false;
360
                    foreach ($key as $v) {
361
                        if ($rs->fields[$v] === null) {
362
                            $has_nulls = true;
363
                            break;
364
                        }
365
                        $keys_array["key[{$v}]"] = $rs->fields[$v];
366
                    }
367
                    if ($has_nulls) {
368
                        echo "<td colspan=\"{$colspan}\">&nbsp;</td>" . "\n";
369
                    } else {
370 View Code Duplication
                        if (isset($actions['actionbuttons']['edit'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
371
                            $actions['actionbuttons']['edit']                            = $edit_params;
372
                            $actions['actionbuttons']['edit']['attr']['href']['urlvars'] = array_merge(
373
                                $actions['actionbuttons']['edit']['attr']['href']['urlvars'],
374
                                $keys_array
375
                            );
376
                        }
377
378 View Code Duplication
                        if (isset($actions['actionbuttons']['delete'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
379
                            $actions['actionbuttons']['delete']                            = $delete_params;
380
                            $actions['actionbuttons']['delete']['attr']['href']['urlvars'] = array_merge(
381
                                $actions['actionbuttons']['delete']['attr']['href']['urlvars'],
382
                                $keys_array
383
                            );
384
                        }
385
386
                        foreach ($actions['actionbuttons'] as $action) {
387
                            echo "<td class=\"opbutton{$id}\">";
388
                            $this->printLink($action, true, __METHOD__);
389
                            echo '</td>' . "\n";
390
                        }
391
                    }
392
                }
393
394
                $this->printTableRowCells($rs, $fkey_information, isset($object));
395
396
                echo '</tr>' . "\n";
397
                $rs->moveNext();
398
                $i++;
399
            }
400
            echo '</table>' . "\n";
401
402
            echo '<p>', $rs->recordCount(), " {$lang['strrows']}</p>" . "\n";
403
            // Show page navigation
404
            $misc->printPages($_REQUEST['page'], $max_pages, $_gets);
405
        } else {
406
            echo "<p>{$lang['strnodata']}</p>" . "\n";
407
        }
408
409
        // Navigation links
410
        $navlinks = [];
411
412
        $fields = [
413
            'server'   => $_REQUEST['server'],
414
            'database' => $_REQUEST['database'],
415
        ];
416
417
        if (isset($_REQUEST['schema'])) {
418
            $fields['schema'] = $_REQUEST['schema'];
419
        }
420
421
        // Return
422 View Code Duplication
        if (isset($_REQUEST['return'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
423
            $urlvars = $misc->getSubjectParams($_REQUEST['return']);
424
425
            $navlinks['back'] = [
426
                'attr'    => [
427
                    'href' => [
428
                        'url'     => $urlvars['url'],
429
                        'urlvars' => $urlvars['params'],
430
                    ],
431
                ],
432
                'content' => $lang['strback'],
433
            ];
434
        }
435
436
        // Edit SQL link
437
        if ($type == 'QUERY') {
438
            $navlinks['edit'] = [
439
                'attr'    => [
440
                    'href' => [
441
                        'url'     => 'database.php',
442
                        'urlvars' => array_merge($fields, [
443
                            'action'   => 'sql',
444
                            'paginate' => 'on',
445
                        ]),
446
                    ],
447
                ],
448
                'content' => $lang['streditsql'],
449
            ];
450
        }
451
452
        // Expand/Collapse
453
        if ($_REQUEST['strings'] == 'expanded') {
454
            $navlinks['collapse'] = [
455
                'attr'    => [
456
                    'href' => [
457
                        'url'     => 'display.php',
458
                        'urlvars' => array_merge(
459
                            $_gets,
460
                            [
461
                                'strings' => 'collapsed',
462
                                'page'    => $_REQUEST['page'],
463
                            ]),
464
                    ],
465
                ],
466
                'content' => $lang['strcollapse'],
467
            ];
468
        } else {
469
            $navlinks['collapse'] = [
470
                'attr'    => [
471
                    'href' => [
472
                        'url'     => 'display.php',
473
                        'urlvars' => array_merge(
474
                            $_gets,
475
                            [
476
                                'strings' => 'expanded',
477
                                'page'    => $_REQUEST['page'],
478
                            ]),
479
                    ],
480
                ],
481
                'content' => $lang['strexpand'],
482
            ];
483
        }
484
485
        // Create view and download
486
        if (isset($_REQUEST['query']) && isset($rs) && is_object($rs) && $rs->recordCount() > 0) {
487
488
            // Report views don't set a schema, so we need to disable create view in that case
489 View Code Duplication
            if (isset($_REQUEST['schema'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
490
                $navlinks['createview'] = [
491
                    'attr'    => [
492
                        'href' => [
493
                            'url'     => 'views.php',
494
                            'urlvars' => array_merge($fields, [
495
                                'action'         => 'create',
496
                                'formDefinition' => $_REQUEST['query'],
497
                            ]),
498
                        ],
499
                    ],
500
                    'content' => $lang['strcreateview'],
501
                ];
502
            }
503
504
            $urlvars = [];
505
            if (isset($_REQUEST['search_path'])) {
506
                $urlvars['search_path'] = $_REQUEST['search_path'];
507
            }
508
509
            $navlinks['download'] = [
510
                'attr'    => [
511
                    'href' => [
512
                        'url'     => 'dataexport.php',
513
                        'urlvars' => array_merge($fields, $urlvars),
514
                    ],
515
                ],
516
                'content' => $lang['strdownload'],
517
            ];
518
        }
519
520
        // Insert
521
        if (isset($object) && (isset($subject) && $subject == 'table')) {
522
            $navlinks['insert'] = [
523
                'attr'    => [
524
                    'href' => [
525
                        'url'     => 'tables.php',
526
                        'urlvars' => array_merge($fields, [
527
                            'action' => 'confinsertrow',
528
                            'table'  => $object,
529
                        ]),
530
                    ],
531
                ],
532
                'content' => $lang['strinsert'],
533
            ];
534
        }
535
536
        // Refresh
537
        $navlinks['refresh'] = [
538
            'attr'    => [
539
                'href' => [
540
                    'url'     => 'display.php',
541
                    'urlvars' => array_merge(
542
                        $_gets,
543
                        [
544
                            'strings' => $_REQUEST['strings'],
545
                            'page'    => $_REQUEST['page'],
546
                        ]),
547
                ],
548
            ],
549
            'content' => $lang['strrefresh'],
550
        ];
551
552
        $this->printNavLinks($navlinks, 'display-browse', get_defined_vars());
553
    }
554
555
    /**
556
     * Show confirmation of edit and perform actual update
557
     */
558
    public function doEditRow($confirm, $msg = '')
0 ignored issues
show
Coding Style introduced by
doEditRow uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doEditRow uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
559
    {
560
        $conf = $this->conf;
561
        $misc = $this->misc;
562
        $lang = $this->lang;
563
        $data = $misc->getDatabaseAccessor();
564
565
        if (is_array($_REQUEST['key'])) {
566
            $key = $_REQUEST['key'];
567
        } else {
568
            $key = unserialize(urldecode($_REQUEST['key']));
0 ignored issues
show
Security introduced by
urldecode($_REQUEST['key']) can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_REQUEST, and Data is passed through urldecode()
    in src/controllers/DisplayController.php on line 568

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
569
        }
570
571
        if ($confirm) {
572
            $this->printTrail($_REQUEST['subject']);
573
            $this->printTitle($lang['streditrow']);
574
            $this->printMsg($msg);
575
576
            $attrs = $data->getTableAttributes($_REQUEST['table']);
577
            $rs    = $data->browseRow($_REQUEST['table'], $key);
578
579 View Code Duplication
            if (($conf['autocomplete'] != 'disable')) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
580
                $fksprops = $misc->getAutocompleteFKProperties($_REQUEST['table']);
581
                if ($fksprops !== false) {
582
                    echo $fksprops['code'];
583
                }
584
            } else {
585
                $fksprops = false;
586
            }
587
588
            echo '<form action="' . SUBFOLDER . '/src/views/display.php" method="post" id="ac_form">' . "\n";
589
590
            /*echo '<p>';
591
            if (!$error) {
592
            echo "<input type=\"submit\" name=\"save\" accesskey=\"r\" value=\"{$lang['strsave']}\" />" . "\n";
593
            }
594
595
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />" . "\n";
596
597
            echo '</p>' . "\n";*/
598
599
            $elements = 0;
600
            $error    = true;
601
            if ($rs->recordCount() == 1 && $attrs->recordCount() > 0) {
602
                echo '<table>' . "\n";
603
604
                // Output table header
605
                echo "<tr><th class=\"data\">{$lang['strcolumn']}</th><th class=\"data\">{$lang['strtype']}</th>";
606
                echo "<th class=\"data\">{$lang['strformat']}</th>" . "\n";
607
                echo "<th class=\"data\">{$lang['strnull']}</th><th class=\"data\">{$lang['strvalue']}</th></tr>";
608
609
                $i = 0;
610
                while (!$attrs->EOF) {
611
                    $attrs->fields['attnotnull'] = $data->phpBool($attrs->fields['attnotnull']);
612
                    $id                          = (($i % 2) == 0 ? '1' : '2');
613
614
                    // Initialise variables
615
                    if (!isset($_REQUEST['format'][$attrs->fields['attname']])) {
616
                        $_REQUEST['format'][$attrs->fields['attname']] = 'VALUE';
617
                    }
618
619
                    echo "<tr class=\"data{$id}\">" . "\n";
620
                    echo '<td style="white-space:nowrap;">', $misc->printVal($attrs->fields['attname']), '</td>';
621
                    echo '<td style="white-space:nowrap;">' . "\n";
622
                    echo $misc->printVal($data->formatType($attrs->fields['type'], $attrs->fields['atttypmod']));
623
                    echo '<input type="hidden" name="types[', htmlspecialchars($attrs->fields['attname']), ']" value="',
624
                    htmlspecialchars($attrs->fields['type']), '" /></td>';
625
                    $elements++;
626
                    echo '<td style="white-space:nowrap;">' . "\n";
627
                    echo '<select name="format[' . htmlspecialchars($attrs->fields['attname']), ']">' . "\n";
628
                    echo '<option value="VALUE"', ($_REQUEST['format'][$attrs->fields['attname']] == 'VALUE') ? ' selected="selected"' : '', ">{$lang['strvalue']}</option>" . "\n";
629
                    $selected = ($_REQUEST['format'][$attrs->fields['attname']] == 'EXPRESSION') ? ' selected="selected"' : '';
630
                    echo '<option value="EXPRESSION"' . $selected . ">{$lang['strexpression']}</option>" . "\n";
631
                    echo "</select>\n</td>" . "\n";
632
                    $elements++;
633
                    echo '<td style="white-space:nowrap;">';
634
                    // Output null box if the column allows nulls (doesn't look at CHECKs or ASSERTIONS)
635
                    if (!$attrs->fields['attnotnull']) {
636
                        // Set initial null values
637
                        if ($_REQUEST['action'] == 'confeditrow' && $rs->fields[$attrs->fields['attname']] === null) {
638
                            $_REQUEST['nulls'][$attrs->fields['attname']] = 'on';
639
                        }
640
                        echo "<label><span><input type=\"checkbox\" name=\"nulls[{$attrs->fields['attname']}]\"",
641
                        isset($_REQUEST['nulls'][$attrs->fields['attname']]) ? ' checked="checked"' : '', ' /></span></label></td>' . "\n";
642
                        $elements++;
643
                    } else {
644
                        echo '&nbsp;</td>';
645
                    }
646
647
                    echo "<td id=\"row_att_{$attrs->fields['attnum']}\" style=\"white-space:nowrap;\">";
648
649
                    $extras = [];
650
651
                    // If the column allows nulls, then we put a JavaScript action on the data field to unset the
652
                    // NULL checkbox as soon as anything is entered in the field.  We use the $elements variable to
653
                    // keep track of which element offset we're up to.  We can't refer to the null checkbox by name
654
                    // as it contains '[' and ']' characters.
655
                    if (!$attrs->fields['attnotnull']) {
656
                        $extras['onChange'] = 'elements[' . ($elements - 1) . '].checked = false;';
657
                    }
658
659
                    if (($fksprops !== false) && isset($fksprops['byfield'][$attrs->fields['attnum']])) {
660
                        $extras['id']           = "attr_{$attrs->fields['attnum']}";
661
                        $extras['autocomplete'] = 'off';
662
                    }
663
664
                    echo $data->printField("values[{$attrs->fields['attname']}]", $rs->fields[$attrs->fields['attname']], $attrs->fields['type'], $extras);
665
666
                    echo '</td>';
667
                    $elements++;
668
                    echo '</tr>' . "\n";
669
                    $i++;
670
                    $attrs->moveNext();
671
                }
672
                echo '</table>' . "\n";
673
674
                $error = false;
675
            } elseif ($rs->recordCount() != 1) {
676
                echo "<p>{$lang['strrownotunique']}</p>" . "\n";
677
            } else {
678
                echo "<p>{$lang['strinvalidparam']}</p>" . "\n";
679
            }
680
681
            echo '<input type="hidden" name="action" value="editrow" />' . "\n";
682
            echo $misc->form;
683
            if (isset($_REQUEST['table'])) {
684
                echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), '" />' . "\n";
685
            }
686
687
            if (isset($_REQUEST['subject'])) {
688
                echo '<input type="hidden" name="subject" value="', htmlspecialchars($_REQUEST['subject']), '" />' . "\n";
689
            }
690
691
            if (isset($_REQUEST['query'])) {
692
                echo '<input type="hidden" name="query" value="', htmlspecialchars($_REQUEST['query']), '" />' . "\n";
693
            }
694
695
            if (isset($_REQUEST['count'])) {
696
                echo '<input type="hidden" name="count" value="', htmlspecialchars($_REQUEST['count']), '" />' . "\n";
697
            }
698
699
            if (isset($_REQUEST['return'])) {
700
                echo '<input type="hidden" name="return" value="', htmlspecialchars($_REQUEST['return']), '" />' . "\n";
701
            }
702
703
            echo '<input type="hidden" name="page" value="', htmlspecialchars($_REQUEST['page']), '" />' . "\n";
704
            echo '<input type="hidden" name="sortkey" value="', htmlspecialchars($_REQUEST['sortkey']), '" />' . "\n";
705
            echo '<input type="hidden" name="sortdir" value="', htmlspecialchars($_REQUEST['sortdir']), '" />' . "\n";
706
            echo '<input type="hidden" name="strings" value="', htmlspecialchars($_REQUEST['strings']), '" />' . "\n";
707
            echo '<input type="hidden" name="key" value="', htmlspecialchars(urlencode(serialize($key))), '" />' . "\n";
708
            echo '<p>';
709
            if (!$error) {
710
                echo "<input type=\"submit\" name=\"save\" accesskey=\"r\" value=\"{$lang['strsave']}\" />" . "\n";
711
            }
712
713
            echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />" . "\n";
714
715 View Code Duplication
            if ($fksprops !== false) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
716
                if ($conf['autocomplete'] != 'default off') {
717
                    echo "<input type=\"checkbox\" id=\"no_ac\" value=\"1\" checked=\"checked\" /><label for=\"no_ac\">{$lang['strac']}</label>" . "\n";
718
                } else {
719
                    echo "<input type=\"checkbox\" id=\"no_ac\" value=\"0\" /><label for=\"no_ac\">{$lang['strac']}</label>" . "\n";
720
                }
721
            }
722
723
            echo '</p>' . "\n";
724
            echo '</form>' . "\n";
725
        } else {
726
            if (!isset($_POST['values'])) {
727
                $_POST['values'] = [];
728
            }
729
730
            if (!isset($_POST['nulls'])) {
731
                $_POST['nulls'] = [];
732
            }
733
734
            $status = $data->editRow($_POST['table'], $_POST['values'], $_POST['nulls'],
735
                $_POST['format'], $_POST['types'], $key);
736
            if ($status == 0) {
737
                $this->doBrowse($lang['strrowupdated']);
738
            } elseif ($status == -2) {
739
                $this->doEditRow(true, $lang['strrownotunique']);
740
            } else {
741
                $this->doEditRow(true, $lang['strrowupdatedbad']);
742
            }
743
        }
744
    }
745
746
    /**
747
     * Show confirmation of drop and perform actual drop
748
     */
749
    public function doDelRow($confirm)
0 ignored issues
show
Coding Style introduced by
doDelRow uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
doDelRow uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
750
    {
751
        $conf = $this->conf;
752
        $misc = $this->misc;
753
        $lang = $this->lang;
754
        $data = $misc->getDatabaseAccessor();
755
756
        if ($confirm) {
757
            $this->printTrail($_REQUEST['subject']);
758
            $this->printTitle($lang['strdeleterow']);
759
760
            $rs = $data->browseRow($_REQUEST['table'], $_REQUEST['key']);
761
762
            echo '<form action="' . SUBFOLDER . '/src/views/display.php" method="post">' . "\n";
763
            echo $misc->form;
764
765
            if ($rs->recordCount() == 1) {
766
                echo "<p>{$lang['strconfdeleterow']}</p>" . "\n";
767
768
                $fkinfo = [];
769
                echo '<table><tr>';
770
                $this->printTableHeaderCells($rs, false, true);
771
                echo '</tr>';
772
                echo '<tr class="data1">' . "\n";
773
                $this->printTableRowCells($rs, $fkinfo, true);
774
                echo '</tr>' . "\n";
775
                echo '</table>' . "\n";
776
                echo '<br />' . "\n";
777
778
                echo '<input type="hidden" name="action" value="delrow" />' . "\n";
779
                echo "<input type=\"submit\" name=\"yes\" value=\"{$lang['stryes']}\" />" . "\n";
780
                echo "<input type=\"submit\" name=\"no\" value=\"{$lang['strno']}\" />" . "\n";
781
            } elseif ($rs->recordCount() != 1) {
782
                echo "<p>{$lang['strrownotunique']}</p>" . "\n";
783
                echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />" . "\n";
784
            } else {
785
                echo "<p>{$lang['strinvalidparam']}</p>" . "\n";
786
                echo "<input type=\"submit\" name=\"cancel\" value=\"{$lang['strcancel']}\" />" . "\n";
787
            }
788
            if (isset($_REQUEST['table'])) {
789
                echo '<input type="hidden" name="table" value="', htmlspecialchars($_REQUEST['table']), '" />' . "\n";
790
            }
791
792
            if (isset($_REQUEST['subject'])) {
793
                echo '<input type="hidden" name="subject" value="', htmlspecialchars($_REQUEST['subject']), '" />' . "\n";
794
            }
795
796
            if (isset($_REQUEST['query'])) {
797
                echo '<input type="hidden" name="query" value="', htmlspecialchars($_REQUEST['query']), '" />' . "\n";
798
            }
799
800
            if (isset($_REQUEST['count'])) {
801
                echo '<input type="hidden" name="count" value="', htmlspecialchars($_REQUEST['count']), '" />' . "\n";
802
            }
803
804
            if (isset($_REQUEST['return'])) {
805
                echo '<input type="hidden" name="return" value="', htmlspecialchars($_REQUEST['return']), '" />' . "\n";
806
            }
807
808
            echo '<input type="hidden" name="page" value="', htmlspecialchars($_REQUEST['page']), '" />' . "\n";
809
            echo '<input type="hidden" name="sortkey" value="', htmlspecialchars($_REQUEST['sortkey']), '" />' . "\n";
810
            echo '<input type="hidden" name="sortdir" value="', htmlspecialchars($_REQUEST['sortdir']), '" />' . "\n";
811
            echo '<input type="hidden" name="strings" value="', htmlspecialchars($_REQUEST['strings']), '" />' . "\n";
812
            echo '<input type="hidden" name="key" value="', htmlspecialchars(urlencode(serialize($_REQUEST['key']))), '" />' . "\n";
813
            echo '</form>' . "\n";
814
        } else {
815
            $status = $data->deleteRow($_POST['table'], unserialize(urldecode($_POST['key'])));
0 ignored issues
show
Security introduced by
urldecode($_POST['key']) can contain request data and is used in unserialized context(s) leading to a potential security vulnerability.

1 path for user data to reach this point

  1. Read from $_POST, and Data is passed through urldecode()
    in src/controllers/DisplayController.php on line 815

Preventing Object Injection Attacks

If you pass raw user-data to unserialize() for example, this can be used to create an object of any class that is available in your local filesystem. For an attacker, classes that have magic methods like __destruct or __wakeup are particularly interesting in such a case, as they can be exploited very easily.

We recommend to not pass user data to such a function. In case of unserialize, better use JSON to transfer data.

General Strategies to prevent injection

In general, it is advisable to prevent any user-data to reach this point. This can be done by white-listing certain values:

if ( ! in_array($value, array('this-is-allowed', 'and-this-too'), true)) {
    throw new \InvalidArgumentException('This input is not allowed.');
}

For numeric data, we recommend to explicitly cast the data:

$sanitized = (integer) $tainted;
Loading history...
816
            if ($status == 0) {
817
                $this->doBrowse($lang['strrowdeleted']);
818
            } elseif ($status == -2) {
819
                $this->doBrowse($lang['strrownotunique']);
820
            } else {
821
                $this->doBrowse($lang['strrowdeletedbad']);
822
            }
823
        }
824
    }
825
826
    /**
827
     * build & return the FK information data structure
828
     * used when deciding if a field should have a FK link or not
829
     * @return [type] [description]
830
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment [type] at position 0 could not be parsed: Unknown type name '[' at position 0 in [type].
Loading history...
831
    public function &getFKInfo()
0 ignored issues
show
Coding Style introduced by
getFKInfo uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
832
    {
833
        $conf = $this->conf;
834
        $misc = $this->misc;
835
        $lang = $this->lang;
836
        $data = $misc->getDatabaseAccessor();
837
838
        // Get the foreign key(s) information from the current table
839
        $fkey_information = ['byconstr' => [], 'byfield' => []];
840
841
        if (isset($_REQUEST['table'])) {
842
            $constraints = $data->getConstraintsWithFields($_REQUEST['table']);
843
            if ($constraints->recordCount() > 0) {
844
                $fkey_information['common_url'] = $misc->getHREF('schema') . '&amp;subject=table';
845
846
                /* build the FK constraints data structure */
847
                while (!$constraints->EOF) {
848
                    $constr = &$constraints->fields;
849
                    if ($constr['contype'] == 'f') {
850
                        if (!isset($fkey_information['byconstr'][$constr['conid']])) {
851
                            $fkey_information['byconstr'][$constr['conid']] = [
852
                                'url_data' => 'table=' . urlencode($constr['f_table']) . '&amp;schema=' . urlencode($constr['f_schema']),
853
                                'fkeys'    => [],
854
                                'consrc'   => $constr['consrc'],
855
                            ];
856
                        }
857
858
                        $fkey_information['byconstr'][$constr['conid']]['fkeys'][$constr['p_field']] = $constr['f_field'];
859
860
                        if (!isset($fkey_information['byfield'][$constr['p_field']])) {
861
                            $fkey_information['byfield'][$constr['p_field']] = [];
862
                        }
863
864
                        $fkey_information['byfield'][$constr['p_field']][] = $constr['conid'];
865
                    }
866
                    $constraints->moveNext();
867
                }
868
            }
869
        }
870
871
        return $fkey_information;
872
    }
873
874
    /**
875
     * Print table header cells
876
     * @param $args - associative array for sort link parameters
877
     *
878
     */
879
    public function printTableHeaderCells(&$rs, $args, $withOid)
0 ignored issues
show
Coding Style introduced by
printTableHeaderCells uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
880
    {
881
        $conf = $this->conf;
882
        $misc = $this->misc;
883
        $lang = $this->lang;
884
        $data = $misc->getDatabaseAccessor();
885
        $j    = 0;
886
887
        foreach ($rs->fields as $k => $v) {
888
            if (($k === $data->id) && (!($withOid && $conf['show_oids']))) {
889
                $j++;
890
                continue;
891
            }
892
            $finfo = $rs->fetchField($j);
893
894
            if ($args === false) {
895
                echo '<th class="data">', $misc->printVal($finfo->name), '</th>' . "\n";
896
            } else {
897
                $args['page']    = $_REQUEST['page'];
898
                $args['sortkey'] = $j + 1;
899
                // Sort direction opposite to current direction, unless it's currently ''
900
                $args['sortdir'] = (
901
                    $_REQUEST['sortdir'] == 'asc'
902
                    and $_REQUEST['sortkey'] == ($j + 1)
903
                ) ? 'desc' : 'asc';
904
905
                $sortLink = http_build_query($args);
906
907
                echo "<th class=\"data\"><a href=\"?{$sortLink}\">"
908
                , $misc->printVal($finfo->name);
909
                if ($_REQUEST['sortkey'] == ($j + 1)) {
910
                    if ($_REQUEST['sortdir'] == 'asc') {
911
                        echo '<img src="' . $misc->icon('RaiseArgument') . '" alt="asc">';
912
                    } else {
913
                        echo '<img src="' . $misc->icon('LowerArgument') . '" alt="desc">';
914
                    }
915
                }
916
                echo '</a></th>' . "\n";
917
            }
918
            $j++;
919
        }
920
921
        reset($rs->fields);
922
    }
923
924
    /* Print data-row cells */
925
    public function printTableRowCells(&$rs, &$fkey_information, $withOid)
0 ignored issues
show
Coding Style introduced by
printTableRowCells uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
926
    {
927
        $conf = $this->conf;
928
        $misc = $this->misc;
929
        $lang = $this->lang;
930
        $data = $misc->getDatabaseAccessor();
931
        $j    = 0;
932
933
        if (!isset($_REQUEST['strings'])) {
934
            $_REQUEST['strings'] = 'collapsed';
935
        }
936
937
        foreach ($rs->fields as $k => $v) {
938
            $finfo = $rs->fetchField($j++);
939
940
            if (($k === $data->id) && (!($withOid && $conf['show_oids']))) {
941
                continue;
942
            } elseif ($v !== null && $v == '') {
943
                echo '<td>&nbsp;</td>';
944
            } else {
945
                echo '<td style="white-space:nowrap;">';
946
947
                if (($v !== null) && isset($fkey_information['byfield'][$k])) {
948
                    foreach ($fkey_information['byfield'][$k] as $conid) {
949
                        $query_params = $fkey_information['byconstr'][$conid]['url_data'];
950
951
                        foreach ($fkey_information['byconstr'][$conid]['fkeys'] as $p_field => $f_field) {
952
                            $query_params .= '&amp;' . urlencode("fkey[{$f_field}]") . '=' . urlencode($rs->fields[$p_field]);
953
                        }
954
955
                        /* $fkey_information['common_url'] is already urlencoded */
956
                        $query_params .= '&amp;' . $fkey_information['common_url'];
957
                        echo '<div style="display:inline-block;">';
958
                        echo '<a class="fk fk_' . htmlentities($conid, ENT_QUOTES, 'UTF-8') . "\" href=\"display.php?{$query_params}\">";
959
                        echo '<img src="' . $misc->icon('ForeignKey') . '" style="vertical-align:middle;" alt="[fk]" title="'
960
                        . htmlentities($fkey_information['byconstr'][$conid]['consrc'], ENT_QUOTES, 'UTF-8')
961
                            . '" />';
962
                        echo '</a>';
963
                        echo '</div>';
964
                    }
965
                    echo $misc->printVal($v, $finfo->type, ['null' => true, 'clip' => ($_REQUEST['strings'] == 'collapsed'), 'class' => 'fk_value']);
966
                } else {
967
                    echo $misc->printVal($v, $finfo->type, ['null' => true, 'clip' => ($_REQUEST['strings'] == 'collapsed')]);
968
                }
969
                echo '</td>';
970
            }
971
        }
972
    }
973
974
    /* Print the FK row, used in ajax requests */
975
    public function doBrowseFK()
0 ignored issues
show
Coding Style introduced by
doBrowseFK uses the super-global variable $_REQUEST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
976
    {
977
        $conf = $this->conf;
978
        $misc = $this->misc;
979
        $lang = $this->lang;
980
        $data = $misc->getDatabaseAccessor();
981
982
        $ops = [];
983
        foreach ($_REQUEST['fkey'] as $x => $y) {
984
            $ops[$x] = '=';
985
        }
986
        $query             = $data->getSelectSQL($_REQUEST['table'], [], $_REQUEST['fkey'], $ops);
987
        $_REQUEST['query'] = $query;
988
989
        $fkinfo = $this->getFKInfo();
990
991
        $max_pages = 1;
992
        // Retrieve page from query.  $max_pages is returned by reference.
993
        $rs = $data->browseQuery('SELECT', $_REQUEST['table'], $_REQUEST['query'],
994
            null, null, 1, 1, $max_pages);
995
996
        echo '<a href="javascript:void(0);" style="display:table-cell;" class="fk_delete"><img alt="[delete]" src="' . $misc->icon('Delete') . '" /></a>' . "\n";
997
        echo '<div style="display:table-cell;">';
998
999
        if (is_object($rs) && $rs->recordCount() > 0) {
1000
            /* we are browsing a referenced table here
1001
             * we should show OID if show_oids is true
1002
             * so we give true to withOid in functions bellow
1003
             */
1004
1005
            echo '<table><tr>';
1006
            $this->printTableHeaderCells($rs, false, true);
1007
            echo '</tr>';
1008
            echo '<tr class="data1">' . "\n";
1009
            $this->printTableRowCells($rs, $fkinfo, true);
1010
            echo '</tr>' . "\n";
1011
            echo '</table>' . "\n";
1012
        } else {
1013
            echo $lang['strnodata'];
1014
        }
1015
1016
        echo '</div>';
1017
1018
        exit;
0 ignored issues
show
Coding Style Compatibility introduced by
The method doBrowseFK() contains an exit expression.

An exit expression should only be used in rare cases. For example, if you write a short command line script.

In most cases however, using an exit expression makes the code untestable and often causes incompatibilities with other libraries. Thus, unless you are absolutely sure it is required here, we recommend to refactor your code to avoid its usage.

Loading history...
1019
    }
1020
}
1021