Passed
Push — develop ( d51928...4c214f )
by Felipe
31:34 queued 23:49
created

ADOdbBase   D

Complexity

Total Complexity 81

Size/Duplication

Total Lines 572
Duplicated Lines 11.89 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 68
loc 572
rs 4.8717
c 0
b 0
f 0
wmc 81
lcom 1
cbo 2

22 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 11 11 1
C setComment() 0 61 18
A setDebug() 0 4 1
A fieldArrayClean() 0 12 3
A arrayClean() 0 11 3
A execute() 0 8 1
A close() 0 4 1
A selectSet() 0 11 2
A selectField() 0 16 3
C delete() 0 39 7
A fieldClean() 0 9 2
A clean() 0 10 2
A escapeBytea() 0 4 1
C insert() 10 41 8
C update() 28 62 11
A beginTransaction() 0 4 1
A endTransaction() 0 4 1
A rollbackTransaction() 0 4 1
A getPlatform() 0 10 2
A dbBool() 0 10 2
A phpBool() 0 4 1
D phpArray() 19 41 9

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like ADOdbBase often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ADOdbBase, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/**
4
 * PHPPgAdmin v6.0.0-beta.48
5
 */
6
7
namespace PHPPgAdmin\Database;
8
9
/**
10
 * @file
11
 * Parent class of all ADODB objects.
12
 *
13
 * Id: ADOdbBase.php,v 1.24 2008/02/20 20:43:10 ioguix Exp $
14
 *
15
 * @package PHPPgAdmin
16
 */
17
class ADOdbBase
1 ignored issue
show
Coding Style introduced by
The property $server_info is not named in camelCase.

This check marks property names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
18
{
19
    use \PHPPgAdmin\Traits\HelperTrait;
20
    use \PHPPgAdmin\Database\Traits\HasTrait;
21
22
    public $lang;
23
    public $conf;
24
    protected $container;
25
    protected $server_info;
26
27
    /**
28
     * Base constructor.
29
     *
30
     * @param \PHPPgAdmin\ADONewConnection $conn        The connection object
31
     * @param mixed                        $container
32
     * @param mixed                        $server_info
33
     */
34 View Code Duplication
    public function __construct(&$conn, $container, $server_info)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in 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...
Coding Style Naming introduced by
The parameter $server_info is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
35
    {
36
        $this->container   = $container;
37
        $this->server_info = $server_info;
38
39
        $this->lang = $container->get('lang');
40
        $this->conf = $container->get('conf');
41
42
        $this->prtrace('instanced connection class');
43
        $this->conn = $conn;
44
    }
45
46
    /**
47
     * Sets the comment for an object in the database.
48
     *
49
     * @pre All parameters must already be cleaned
50
     *
51
     * @param string      $obj_type One of 'TABLE' | 'COLUMN' | 'VIEW' | 'SCHEMA' | 'SEQUENCE' | 'TYPE' | 'FUNCTION' | 'AGGREGATE'
52
     * @param string      $obj_name the name of the object for which to attach a comment
53
     * @param string      $table    Name of table that $obj_name belongs to.  Ignored unless $obj_type is 'TABLE' or 'COLUMN'.
54
     * @param string      $comment  the comment to add
55
     * @param null|string $basetype
56
     *
57
     * @return int|\PHPPgAdmin\ADORecordSet recordset of results or error code
58
     */
59
    public function setComment($obj_type, $obj_name, $table, $comment, $basetype = null)
0 ignored issues
show
Coding Style Naming introduced by
The parameter $obj_type is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
Coding Style Naming introduced by
The parameter $obj_name is not named in camelCase.

This check marks parameter names that have not been written in camelCase.

In camelCase names are written without any punctuation, the start of each new word being marked by a capital letter. Thus the name database connection string becomes databaseConnectionString.

Loading history...
60
    {
61
        $sql = "COMMENT ON {$obj_type} ";
62
63
        $f_schema = $this->_schema;
64
        $this->fieldClean($f_schema);
65
        $this->clean($comment); // Passing in an already cleaned comment will lead to double escaped data
66
        // So, while counter-intuitive, it is important to not clean comments before
67
        // calling setComment. We will clean it here instead.
68
        /*
69
         * $this->fieldClean($table);
70
         * $this->fieldClean($obj_name);
71
         */
72
73
        switch ($obj_type) {
74
            case 'TABLE':
75
                $sql .= "\"{$f_schema}\".\"{$table}\" IS ";
76
77
                break;
78
            case 'COLUMN':
79
                $sql .= "\"{$f_schema}\".\"{$table}\".\"{$obj_name}\" IS ";
80
81
                break;
82
            case 'SEQUENCE':
83
            case 'VIEW':
84
            case 'MATERIALIZED VIEW':
85
            case 'TEXT SEARCH CONFIGURATION':
86
            case 'TEXT SEARCH DICTIONARY':
87
            case 'TEXT SEARCH TEMPLATE':
88
            case 'TEXT SEARCH PARSER':
89
            case 'TYPE':
90
                $sql .= "\"{$f_schema}\".";
91
            // no break
92
            case 'DATABASE':
93
            case 'ROLE':
94
            case 'SCHEMA':
95
            case 'TABLESPACE':
96
                $sql .= "\"{$obj_name}\" IS ";
97
98
                break;
99
            case 'FUNCTION':
100
                $sql .= "\"{$f_schema}\".{$obj_name} IS ";
101
102
                break;
103
            case 'AGGREGATE':
104
                $sql .= "\"{$f_schema}\".\"{$obj_name}\" (\"{$basetype}\") IS ";
105
106
                break;
107
            default:
108
                // Unknown object type
109
                return -1;
110
        }
111
112
        if ($comment != '') {
113
            $sql .= "'{$comment}';";
114
        } else {
115
            $sql .= 'NULL;';
116
        }
117
118
        return $this->execute($sql);
119
    }
120
121
    /**
122
     * Turns on or off query debugging.
123
     *
124
     * @param bool $debug True to turn on debugging, false otherwise
125
     */
126
    public function setDebug($debug)
127
    {
128
        $this->conn->debug = $debug;
129
    }
130
131
    /**
132
     * Cleans (escapes) an array of field names.
133
     *
134
     * @param array $arr The array to clean, by reference
135
     *
136
     * @return array The cleaned array
137
     */
138
    public function fieldArrayClean(&$arr)
139
    {
140
        foreach ($arr as $k => $v) {
141
            if ($v === null) {
142
                continue;
143
            }
144
145
            $arr[$k] = str_replace('"', '""', $v);
146
        }
147
148
        return $arr;
149
    }
150
151
    /**
152
     * Cleans (escapes) an array.
153
     *
154
     * @param array $arr The array to clean, by reference
155
     *
156
     * @return array The cleaned array
157
     */
158
    public function arrayClean(&$arr)
159
    {
160
        foreach ($arr as $k => $v) {
161
            if ($v === null) {
162
                continue;
163
            }
164
            $arr[$k] = pg_escape_string($v);
165
        }
166
167
        return $arr;
168
    }
169
170
    /**
171
     * Executes a query on the underlying connection.
172
     *
173
     * @param string $sql The SQL query to execute
174
     *
175
     * @return \PHPPgAdmin\ADORecordSet A recordset
176
     */
177
    public function execute($sql)
178
    {
179
        // Execute the statement
180
        $rs = $this->conn->Execute($sql);
0 ignored issues
show
Unused Code introduced by
$rs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
181
182
        // If failure, return error value
183
        return $this->conn->ErrorNo();
184
    }
185
186
    /**
187
     * Closes the connection the database class
188
     * relies on.
189
     */
190
    public function close()
191
    {
192
        $this->conn->close();
193
    }
194
195
    /**
196
     * Retrieves a ResultSet from a query.
197
     *
198
     * @param string $sql The SQL statement to be executed
199
     *
200
     * @return int|\PHPPgAdmin\ADORecordSet A recordset or an error number
201
     */
202
    public function selectSet($sql)
203
    {
204
        // Execute the statement
205
        $rs = $this->conn->Execute($sql);
206
207
        if (!$rs) {
208
            return $this->conn->ErrorNo();
209
        }
210
211
        return $rs;
212
    }
213
214
    /**
215
     * Retrieves a single value from a query.
216
     *
217
     * @@ assumes that the query will return only one row - returns field value in the first row
218
     *
219
     * @param string $sql   The SQL statement to be executed
220
     * @param string $field The field name to be returned
221
     *
222
     * @return int|string single field value, error number on error or -1 if no rows where found
223
     */
224
    public function selectField($sql, $field)
225
    {
226
        // Execute the statement
227
        $rs = $this->conn->Execute($sql);
228
229
        // If failure, or no rows returned, return error value
230
        if (!$rs) {
231
            return $this->conn->ErrorNo();
232
        }
233
234
        if ($rs->RecordCount() == 0) {
235
            return -1;
236
        }
237
238
        return $rs->fields[$field];
239
    }
240
241
    /**
242
     * Delete from the database.
243
     *
244
     * @param string $table      The name of the table
245
     * @param array  $conditions (array) A map of field names to conditions
246
     * @param string $schema     (optional) The table's schema
247
     *
248
     * @return int 0 success
249
     */
250
    public function delete($table, $conditions, $schema = '')
251
    {
252
        $this->fieldClean($table);
253
254
        reset($conditions);
255
256
        if (!empty($schema)) {
257
            $this->fieldClean($schema);
258
            $schema = "\"{$schema}\".";
259
        }
260
261
        // Build clause
262
        $sql = '';
263
        //while (list($key, $value) = each($conditions)) {
264
        foreach ($conditions as $key => $value) {
265
            $this->clean($key);
266
            $this->clean($value);
267
            if ($sql) {
268
                $sql .= " AND \"{$key}\"='{$value}'";
269
            } else {
270
                $sql = "DELETE FROM {$schema}\"{$table}\" WHERE \"{$key}\"='{$value}'";
271
            }
272
        }
273
274
        // Check for failures
275
        if (!$this->conn->Execute($sql)) {
276
            // Check for referential integrity failure
277
            if (stristr($this->conn->ErrorMsg(), 'referential')) {
278
                return -1;
279
            }
280
        }
281
282
        // Check for no rows modified
283
        if ($this->conn->Affected_Rows() == 0) {
284
            return -2;
285
        }
286
287
        return $this->conn->ErrorNo();
288
    }
289
290
    /**
291
     * Cleans (escapes) an object name (eg. table, field).
292
     *
293
     * @param null|string $str The string to clean, by reference
294
     *
295
     * @return null|string The cleaned string
296
     */
297
    public function fieldClean(&$str)
298
    {
299
        if ($str === null) {
300
            return null;
301
        }
302
        $str = str_replace('"', '""', $str);
303
304
        return $str;
305
    }
306
307
    /**
308
     * Cleans (escapes) a string.
309
     *
310
     * @param null|string $str The string to clean, by reference
311
     *
312
     * @return null|string The cleaned string
313
     */
314
    public function clean(&$str)
315
    {
316
        if ($str === null) {
317
            return null;
318
        }
319
        $str = str_replace("\r\n", "\n", $str);
320
        $str = pg_escape_string($str);
321
322
        return $str;
323
    }
324
325
    /**
326
     * Escapes bytea data for display on the screen.
327
     *
328
     * @param string $data The bytea data
329
     *
330
     * @return string Data formatted for on-screen display
331
     */
332
    public function escapeBytea($data)
333
    {
334
        return htmlentities($data, ENT_QUOTES, 'UTF-8');
335
    }
336
337
    /**
338
     * Insert a set of values into the database.
339
     *
340
     * @param string $table The table to insert into
341
     * @param array  $vars  (array) A mapping of the field names to the values to be inserted
342
     *
343
     * @return int 0 success
344
     */
345
    public function insert($table, $vars)
346
    {
347
        $this->fieldClean($table);
348
        $sql = '';
349
        // Build clause
350
        if (sizeof($vars) > 0) {
351
            $fields = '';
352
            $values = '';
353
            foreach ($vars as $key => $value) {
354
                $this->clean($key);
355
                $this->clean($value);
356
357
                if ($fields) {
358
                    $fields .= ", \"{$key}\"";
359
                } else {
360
                    $fields = "INSERT INTO \"{$table}\" (\"{$key}\"";
361
                }
362
363
                if ($values) {
364
                    $values .= ", '{$value}'";
365
                } else {
366
                    $values = ") VALUES ('{$value}'";
367
                }
368
            }
369
            $sql .= $fields.$values.')';
370
        }
371
372
        // Check for failures
373 View Code Duplication
        if (!$this->conn->Execute($sql)) {
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...
374
            // Check for unique constraint failure
375
            if (stristr($this->conn->ErrorMsg(), 'unique')) {
376
                return -1;
377
            }
378
379
            if (stristr($this->conn->ErrorMsg(), 'referential')) {
380
                return -2;
381
            } // Check for referential integrity failure
382
        }
383
384
        return $this->conn->ErrorNo();
385
    }
386
387
    /**
388
     * Update a row in the database.
389
     *
390
     * @param string $table The table that is to be updated
391
     * @param array  $vars  (array) A mapping of the field names to the values to be updated
392
     * @param array  $where (array) A mapping of field names to values for the where clause
393
     * @param array  $nulls (array, optional) An array of fields to be set null
394
     *
395
     * @return int 0 success
396
     */
397
    public function update($table, $vars, $where, $nulls = [])
398
    {
399
        $this->fieldClean($table);
400
401
        $setClause   = '';
402
        $whereClause = '';
403
404
        // Populate the syntax arrays
405
        reset($vars);
406
        //while (list($key, $value) = each($vars)) {
407 View Code Duplication
        foreach ($vars as $key => $value) {
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...
408
            $this->fieldClean($key);
409
            $this->clean($value);
410
            if ($setClause) {
411
                $setClause .= ", \"{$key}\"='{$value}'";
412
            } else {
413
                $setClause = "UPDATE \"{$table}\" SET \"{$key}\"='{$value}'";
414
            }
415
        }
416
417
        reset($nulls);
418
        //while (list(, $value) = each($nulls)) {
419
        foreach ($nulls as $key => $value) {
420
            $this->fieldClean($value);
421
            if ($setClause) {
422
                $setClause .= ", \"{$value}\"=NULL";
423
            } else {
424
                $setClause = "UPDATE \"{$table}\" SET \"{$value}\"=NULL";
425
            }
426
        }
427
428
        reset($where);
429
        //while (list($key, $value) = each($where)) {
430 View Code Duplication
        foreach ($where as $key => $value) {
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...
431
            $this->fieldClean($key);
432
            $this->clean($value);
433
            if ($whereClause) {
434
                $whereClause .= " AND \"{$key}\"='{$value}'";
435
            } else {
436
                $whereClause = " WHERE \"{$key}\"='{$value}'";
437
            }
438
        }
439
440
        // Check for failures
441 View Code Duplication
        if (!$this->conn->Execute($setClause.$whereClause)) {
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...
442
            // Check for unique constraint failure
443
            if (stristr($this->conn->ErrorMsg(), 'unique')) {
444
                return -1;
445
            }
446
447
            if (stristr($this->conn->ErrorMsg(), 'referential')) {
448
                return -2;
449
            } // Check for referential integrity failure
450
        }
451
452
        // Check for no rows modified
453
        if ($this->conn->Affected_Rows() == 0) {
454
            return -3;
455
        }
456
457
        return $this->conn->ErrorNo();
458
    }
459
460
    /**
461
     * Begin a transaction.
462
     *
463
     * @return bool 0 success
464
     */
465
    public function beginTransaction()
466
    {
467
        return !$this->conn->BeginTrans();
468
    }
469
470
    /**
471
     * End a transaction.
472
     *
473
     * @return bool 0 success
474
     */
475
    public function endTransaction()
476
    {
477
        return !$this->conn->CommitTrans();
478
    }
479
480
    /**
481
     * Roll back a transaction.
482
     *
483
     * @return bool 0 success
484
     */
485
    public function rollbackTransaction()
486
    {
487
        return !$this->conn->RollbackTrans();
488
    }
489
490
    /**
491
     * Get the backend platform.
492
     *
493
     * @return string The backend platform
494
     */
495
    public function getPlatform()
496
    {
497
        try {
498
            return $this->conn->platform;
499
        } catch (\Exception $e) {
0 ignored issues
show
Unused Code introduced by
catch (\Exception $e) { ... return 'UNKNOWN'; } does not seem to be reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
500
            $this->prtrace($e->getMessage());
501
502
            return 'UNKNOWN';
503
        }
504
    }
505
506
    // Type conversion routines
507
508
    /**
509
     * Change the value of a parameter to database representation depending on whether it evaluates to true or false.
510
     *
511
     * @param mixed $parameter the parameter
512
     *
513
     * @return string boolean  database representation
514
     */
515
    public function dbBool(&$parameter)
516
    {
517
        if ($parameter) {
518
            $parameter = 't';
519
        } else {
520
            $parameter = 'f';
521
        }
522
523
        return $parameter;
524
    }
525
526
    /**
527
     * Change a parameter from database representation to a boolean, (others evaluate to false).
528
     *
529
     * @param string $parameter the parameter
530
     *
531
     * @return bool
532
     */
533
    public function phpBool($parameter)
534
    {
535
        return $parameter === 't';
536
    }
537
538
    /**
539
     * Change a db array into a PHP array.
540
     *
541
     * @param string $dbarr
542
     *
543
     * @return array A PHP array
544
     *
545
     * @internal param String $arr representing the DB array
546
     */
547
    public function phpArray($dbarr)
548
    {
549
        // Take off the first and last characters (the braces)
550
        $arr = substr($dbarr, 1, strlen($dbarr) - 2);
551
552
        // Pick out array entries by carefully parsing.  This is necessary in order
553
        // to cope with double quotes and commas, etc.
554
        $elements  = [];
555
        $i         = $j         = 0;
556
        $in_quotes = false;
557 View Code Duplication
        while ($i < strlen($arr)) {
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...
558
            // If current char is a double quote and it's not escaped, then
559
            // enter quoted bit
560
            $char = substr($arr, $i, 1);
561
            if ($char == '"' && ($i == 0 || substr($arr, $i - 1, 1) != '\\')) {
562
                $in_quotes = !$in_quotes;
563
            } elseif ($char == ',' && !$in_quotes) {
564
                // Add text so far to the array
565
                $elements[] = substr($arr, $j, $i - $j);
566
                $j          = $i + 1;
567
            }
568
            ++$i;
569
        }
570
        // Add final text to the array
571
        $elements[] = substr($arr, $j);
572
573
        $elementcount = sizeof($elements);
574
        // Do one further loop over the elements array to remote double quoting
575
        // and escaping of double quotes and backslashes
576
        for ($i = 0; $i < $elementcount; ++$i) {
577
            $v = $elements[$i];
578 View Code Duplication
            if (strpos($v, '"') === 0) {
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...
579
                $v            = substr($v, 1, strlen($v) - 2);
580
                $v            = str_replace('\\"', '"', $v);
581
                $v            = str_replace('\\\\', '\\', $v);
582
                $elements[$i] = $v;
583
            }
584
        }
585
586
        return $elements;
587
    }
588
}
589