Completed
Push — master ( 0cc82e...4ac1c9 )
by Doug
30:45 queued 29:12
created

MySQLPDODatabase::getIndexes()   A

Complexity

Conditions 5
Paths 9

Size

Total Lines 35
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 5.9256

Importance

Changes 0
Metric Value
cc 5
eloc 17
nc 9
nop 2
dl 0
loc 35
ccs 12
cts 18
cp 0.6667
crap 5.9256
rs 9.3888
c 0
b 0
f 0
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
/**
12
 * MySQL database connection (PDO).
13
 * @author Doug Wright
14
 */
15
class MySQLPDODatabase extends PDODatabase
16
{
17
    /**
18
     * Character to use when quoting identifiers.
19
     */
20
    public const IDENTIFIER_OPENQUOTE = '`';
21
22
    /**
23
     * Character to use when quoting identifiers.
24
     */
25
    public const IDENTIFIER_CLOSEQUOTE = '`';
26
27
    /**
28
     * Constructor.
29
     * @param string $aHost            hostname to connect to
30
     * @param int    $aPort            port number to connect to
31
     * @param string $aDefaultDatabase name of default database to use
32
     * @param string $aUsername        connection username
33
     * @param string $aPassword        connection password
34
     * @param string $aCharset         connection character set
35
     */
36 40
    public function __construct($aHost, $aPort, $aDefaultDatabase, $aUsername, $aPassword, $aCharset = 'utf8mb4')
37
    {
38 40
        parent::__construct("mysql:host={$aHost};port={$aPort};dbname={$aDefaultDatabase};charset={$aCharset}", $aUsername, $aPassword);
39 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

39
        self::/** @scrutinizer ignore-call */ 
40
              setAttribute(\PDO::ATTR_EMULATE_PREPARES, false);
Loading history...
40 40
    }
41
42
    /**
43
     * Escapes/quotes a parameter for use in a query.
44
     * @param  mixed  $aParam     the parameter to be quoted
45
     * @param  int    $aParamType data type hint for drivers
46
     * @return string a quoted string that is theoretically safe to pass into an SQL statement
47
     */
48 12
    public function escape($aParam, $aParamType = DatabaseInterface::PARAM_IS_STR)
49
    {
50
        switch ($aParamType) {
51 12
            case self::PARAM_IS_BLOB:
52 4
                return '0x' . bin2hex($aParam); //avoid any possible charset mess
53
                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...
54
55
            default:
56 12
                return parent::escape($aParam, $aParamType);
57
        }
58
    }
59
60
    /**
61
     * List of tables in a database.
62
     * @param  string $aDatabase database/schema name
63
     * @return array
64
     */
65 8
    public function getTables($aDatabase = null)
66
    {
67 8
        if ($aDatabase) {
68 4
            $statement = $this->prepare('SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = :database ORDER BY TABLE_NAME ASC');
69 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

69
            $statement->/** @scrutinizer ignore-call */ 
70
                        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...
70 4
            $statement->execute();
71
        } else {
72 4
            $statement = $this->query('SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES');
73
        }
74
75 8
        $result = $statement->fetchAssoc(true, true);
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

75
        /** @scrutinizer ignore-call */ 
76
        $result = $statement->fetchAssoc(true, true);

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...
76
77 8
        $tables = [];
78 8
        foreach ($result as $database => $dbtables) {
79 8
            $tables[$database] = [];
80 8
            foreach ($dbtables as $table) {
81 8
                $tables[$database][] = $table['TABLE_NAME'];
82
            }
83
        }
84
85 8
        return $aDatabase ? $tables[$aDatabase] : $tables;
86
    }
87
88
    /**
89
     * List of columns (and types) in a table.
90
     * @param  string                $aDatabase database/schema name
91
     * @param  string                $aTable    table name
92
     * @return ColumnMetaInterface[]
93
     */
94 16
    public function getTableColumns($aDatabase, $aTable)
95
    {
96 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');
97 16
        $statement->bindParamToValue(':database', $aDatabase);
98 16
        $statement->bindParamToValue(':table_name', $aTable);
99 16
        $statement->execute();
100
101 16
        $result = $statement->fetchAssoc();
102 16
        $columns = [];
103 16
        foreach ($result as $row) {
104 16
            $columns[$row['COLUMN_NAME']] = new MySQLColumnMeta($this, $aDatabase, $aTable, $row['COLUMN_NAME']);
105
        }
106
107 16
        return $columns;
108
    }
109
110
    /**
111
     * Primary key column(s).
112
     * @param  string $aDatabase database/schema name
113
     * @param  string $aTable    table name
114
     * @return array
115
     */
116 16
    public function getPrimaryKey($aDatabase, $aTable)
117
    {
118 16
        $columns = [];
119 16
        $SQL = "SELECT ORDINAL_POSITION, COLUMN_NAME
120
                     FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
121
                     WHERE KEY_COLUMN_USAGE.CONSTRAINT_SCHEMA = :database
122
                           AND KEY_COLUMN_USAGE.TABLE_NAME = :table_name
123
                           AND KEY_COLUMN_USAGE.CONSTRAINT_NAME = 'PRIMARY'
124
                     ORDER BY ORDINAL_POSITION";
125 16
        $statement = $this->prepare($SQL);
126 16
        $statement->bindParamToValue(':database', $aDatabase);
127 16
        $statement->bindParamToValue(':table_name', $aTable);
128 16
        $statement->execute();
129
130 16
        $result = $statement->fetchAssoc();
131 16
        foreach ($result as $column) {
132
            $columns[] = $column['COLUMN_NAME'];
133
        }
134
135 16
        return $columns;
136
    }
137
138
    /**
139
     * Non-PK indexes.
140
     * @param  string $aDatabase database/schema name
141
     * @param  string $aTable    table name
142
     * @return array
143
     */
144 16
    public function getIndexes($aDatabase, $aTable)
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