MySQLPDODatabase::getPrimaryKey()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 20
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 2.003

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 20
ccs 10
cts 11
cp 0.9091
crap 2.003
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
/**
5
 * Database Access Layer.
6
 * @author Doug Wright
7
 */
8
9
namespace DVDoug\DB;
10
11
use function bin2hex;
12
use PDO;
13
14
/**
15
 * MySQL database connection (PDO).
16
 * @author Doug Wright
17
 */
18
class MySQLPDODatabase extends PDODatabase
19
{
20
    /**
21
     * Character to use when quoting identifiers.
22
     */
23
    public const IDENTIFIER_OPENQUOTE = '`';
24
25
    /**
26
     * Character to use when quoting identifiers.
27
     */
28
    public const IDENTIFIER_CLOSEQUOTE = '`';
29
30
    /**
31
     * Constructor.
32
     * @param string $aHost            hostname to connect to
33
     * @param int    $aPort            port number to connect to
34
     * @param string $aDefaultDatabase name of default database to use
35
     * @param string $aUsername        connection username
36
     * @param string $aPassword        connection password
37
     * @param string $aCharset         connection character set
38
     */
39 40
    public function __construct($aHost, $aPort, $aDefaultDatabase, $aUsername, $aPassword, $aCharset = 'utf8mb4')
40
    {
41 40
        parent::__construct("mysql:host={$aHost};port={$aPort};dbname={$aDefaultDatabase};charset={$aCharset}", $aUsername, $aPassword);
42 40
        self::setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
0 ignored issues
show
Bug Best Practice introduced by
The method PDO::setAttribute() is not static, but was called statically. ( Ignorable by Annotation )

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

42
        self::/** @scrutinizer ignore-call */ 
43
              setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
Loading history...
43 40
    }
44
45
    /**
46
     * Escapes/quotes a parameter for use in a query.
47
     * @param  mixed  $aParam     the parameter to be quoted
48
     * @param  int    $aParamType data type hint for drivers
49
     * @return string a quoted string that is theoretically safe to pass into an SQL statement
50
     */
51 12
    public function escape($aParam, $aParamType = DatabaseInterface::PARAM_IS_STR)
52
    {
53
        switch ($aParamType) {
54 12
            case self::PARAM_IS_BLOB:
55 4
                return '0x' . bin2hex($aParam); //avoid any possible charset mess
56
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
57
58
            default:
59 12
                return parent::escape($aParam, $aParamType);
60
        }
61
    }
62
63
    /**
64
     * List of tables in a database.
65
     * @param string $aDatabase database/schema name
66
     */
67 8
    public function getTables($aDatabase = null): array
68
    {
69 8
        if ($aDatabase) {
70 4
            $statement = $this->prepare('SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = :database ORDER BY TABLE_NAME ASC');
71 4
            $statement->bindParamToValue(':database', $aDatabase);
0 ignored issues
show
Bug introduced by
The method bindParamToValue() does not exist on PDOStatement. Did you maybe mean bindParam()? ( Ignorable by Annotation )

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

71
            $statement->/** @scrutinizer ignore-call */ 
72
                        bindParamToValue(':database', $aDatabase);

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

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

Loading history...
72 4
            $statement->execute();
73
        } else {
74 4
            $statement = $this->query('SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES');
75
        }
76
77 8
        $result = $statement->fetchAssoc(true, true);
78
79 8
        $tables = [];
80 8
        foreach ($result as $database => $dbtables) {
81 8
            $tables[$database] = [];
82 8
            foreach ($dbtables as $table) {
83 8
                $tables[$database][] = $table['TABLE_NAME'];
84
            }
85
        }
86
87 8
        return $aDatabase ? $tables[$aDatabase] : $tables;
88
    }
89
90
    /**
91
     * List of columns (and types) in a table.
92
     * @param  string                $aDatabase database/schema name
93
     * @param  string                $aTable    table name
94
     * @return ColumnMetaInterface[]
95
     */
96 16
    public function getTableColumns($aDatabase, $aTable): array
97
    {
98 16
        $statement = $this->prepare('SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = :database AND TABLE_NAME = :table_name ORDER BY ORDINAL_POSITION ASC');
99 16
        $statement->bindParamToValue(':database', $aDatabase);
100 16
        $statement->bindParamToValue(':table_name', $aTable);
101 16
        $statement->execute();
102
103 16
        $result = $statement->fetchAssoc();
0 ignored issues
show
Bug introduced by
The method fetchAssoc() does not exist on PDOStatement. Did you maybe mean fetch()? ( Ignorable by Annotation )

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

103
        /** @scrutinizer ignore-call */ 
104
        $result = $statement->fetchAssoc();

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

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

Loading history...
104 16
        $columns = [];
105 16
        foreach ($result as $row) {
106 16
            $columns[$row['COLUMN_NAME']] = new MySQLColumnMeta($this, $aDatabase, $aTable, $row['COLUMN_NAME']);
107
        }
108
109 16
        return $columns;
110
    }
111
112
    /**
113
     * Primary key column(s).
114
     * @param string $aDatabase database/schema name
115
     * @param string $aTable    table name
116
     */
117 16
    public function getPrimaryKey($aDatabase, $aTable): array
118
    {
119 16
        $columns = [];
120 16
        $SQL = "SELECT ORDINAL_POSITION, COLUMN_NAME
121
                     FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
122
                     WHERE KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA = :database
123
                           AND KEY_COLUMN_USAGE.TABLE_NAME = :table_name
124
                           AND KEY_COLUMN_USAGE.CONSTRAINT_NAME = 'PRIMARY'
125
                     ORDER BY ORDINAL_POSITION";
126 16
        $statement = $this->prepare($SQL);
127 16
        $statement->bindParamToValue(':database', $aDatabase);
128 16
        $statement->bindParamToValue(':table_name', $aTable);
129 16
        $statement->execute();
130
131 16
        $result = $statement->fetchAssoc();
132 16
        foreach ($result as $column) {
133
            $columns[] = $column['COLUMN_NAME'];
134
        }
135
136 16
        return $columns;
137
    }
138
139
    /**
140
     * Non-PK indexes.
141
     * @param string $aDatabase database/schema name
142
     * @param string $aTable    table name
143
     */
144 16
    public function getIndexes($aDatabase, $aTable): array
145
    {
146 16
        $indexes = [];
147 16
        $SQL = "SELECT INDEX_NAME, COLUMN_NAME
148
                     FROM INFORMATION_SCHEMA.STATISTICS
149
                     WHERE TABLE_SCHEMA = :database
150
                           AND TABLE_NAME = :table_name
151
                           AND INDEX_NAME != 'PRIMARY'
152
                     ORDER BY INDEX_NAME ASC, SEQ_IN_INDEX ASC";
153 16
        $statement = $this->prepare($SQL);
154 16
        $statement->bindParamToValue(':database', $aDatabase);
155 16
        $statement->bindParamToValue(':table_name', $aTable);
156 16
        $statement->execute();
157
158 16
        $result = $statement->fetchAssoc(true, true);
159
160 16
        foreach ($result as $index => $columnList) {
161
            $indexes[$index] = [];
162
            foreach ($columnList as $col) {
163
                $indexes[$index][] = $col['COLUMN_NAME'];
164
            }
165
        }
166
167
        /*
168
         * Subtract PK if any
169
         */
170 16
        $PK = $this->getPrimaryKey($aDatabase, $aTable);
171 16
        foreach ($indexes as $name => $columns) {
172
            if ($PK === $columns) {
173
                unset($indexes[$name]);
174
                break;
175
            }
176
        }
177
178 16
        return $indexes;
179
    }
180
}
181