Completed
Branch FET/asset-manager (433489)
by
unknown
32:42 queued 18:11
created

TableManager::dropIndexIfSizeNot()   C

Complexity

Conditions 7
Paths 10

Size

Total Lines 24
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 17
nc 10
nop 4
dl 0
loc 24
rs 6.7272
c 0
b 0
f 0
1
<?php
2
3
namespace EventEspresso\core\services\database;
4
5
/**
6
 * Class TableManager
7
 * For performing mysql database table schema manipulation
8
 *
9
 * @package               Event Espresso
10
 * @subpackage
11
 * @author                Mike Nelson
12
 */
13
class TableManager extends \EE_Base
14
{
15
16
    /**
17
     * @var TableAnalysis $table_analysis
18
     */
19
    private $table_analysis;
20
21
22
    /**
23
     * TableManager constructor.
24
     *
25
     * @param TableAnalysis $TableAnalysis
26
     */
27
    public function __construct(TableAnalysis $TableAnalysis)
28
    {
29
        $this->table_analysis = $TableAnalysis;
30
    }
31
32
33
    /**
34
     * Gets the injected table analyzer, or throws an exception
35
     *
36
     * @return TableAnalysis
37
     * @throws \EE_Error
38
     */
39 View Code Duplication
    protected function getTableAnalysis()
40
    {
41
        if ($this->table_analysis instanceof TableAnalysis) {
42
            return $this->table_analysis;
43
        } else {
44
            throw new \EE_Error(
45
                sprintf(
46
                    __('Table analysis class on class %1$s is not set properly.', 'event_espresso'),
47
                    get_class($this)
48
                )
49
            );
50
        }
51
    }
52
53
54
    /**
55
     * @param string $table_name which can optionally start with $wpdb->prefix or not
56
     * @param string $column_name
57
     * @param string $column_info
58
     * @return bool|false|int
59
     */
60
    public function addColumn($table_name, $column_name, $column_info = 'INT UNSIGNED NOT NULL')
61
    {
62
        if (apply_filters('FHEE__EEH_Activation__add_column_if_it_doesnt_exist__short_circuit', false)) {
63
            return false;
64
        }
65
        global $wpdb;
66
        $full_table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
67
        $columns = $this->getTableColumns($table_name);
68
        if (! in_array($column_name, $columns)) {
69
            $alter_query = "ALTER TABLE {$full_table_name} ADD {$column_name} {$column_info}";
70
            return $wpdb->query($alter_query);
71
        }
72
        return true;
73
    }
74
75
76
    /**
77
     * Gets the name of all columns on the  table. $table_name can
78
     * optionally start with $wpdb->prefix or not
79
     *
80
     * @global \wpdb $wpdb
81
     * @param string $table_name
82
     * @return array
83
     */
84
    public function getTableColumns($table_name)
85
    {
86
        global $wpdb;
87
        $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
88
        $field_array = array();
89
        if (! empty($table_name)) {
90
            $columns = $wpdb->get_results("SHOW COLUMNS FROM {$table_name} ");
91
            if ($columns !== false) {
92
                foreach ($columns as $column) {
93
                    $field_array[] = $column->Field;
94
                }
95
            }
96
        }
97
        return $field_array;
98
    }
99
100
101
    /**
102
     * Drops the specified table from the database. $table_name can
103
     * optionally start with $wpdb->prefix or not
104
     *
105
     * @global \wpdb $wpdb
106
     * @param string $table_name
107
     * @return int
108
     */
109
    public function dropTable($table_name)
110
    {
111
        global $wpdb;
112
        if ($this->getTableAnalysis()->tableExists($table_name)) {
113
            $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
114
            return $wpdb->query("DROP TABLE IF EXISTS {$table_name}");
115
        }
116
        return 0;
117
    }
118
119
120
    /**
121
     * Drops all the tables mentioned in a single MYSQL query. Double-checks
122
     * each table name provided has a wpdb prefix attached, and that it exists.
123
     * Returns the list actually deleted
124
     *
125
     * @global WPDB $wpdb
126
     * @param array $table_names
127
     * @return array of table names which we deleted
128
     */
129
    public function dropTables($table_names)
130
    {
131
        $tables_to_delete = array();
132
        foreach ($table_names as $table_name) {
133
            $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
134
            if ($this->getTableAnalysis()->tableExists($table_name)) {
135
                $tables_to_delete[ $table_name ] = $table_name;
136
            }
137
        }
138
        if (! empty($tables_to_delete)) {
139
            global $wpdb;
140
            // make sure we only have a unique strings in the array.
141
            $tables_to_delete = array_unique($tables_to_delete);
142
            $wpdb->query('DROP TABLE ' . implode(', ', $tables_to_delete));
143
        }
144
        return $tables_to_delete;
145
    }
146
147
148
    /**
149
     * Drops the specified index from the specified table. $table_name can
150
     * optionally start with $wpdb->prefix or not
151
     *
152
     * @global \wpdb $wpdb
153
     * @param string $table_name
154
     * @param string $index_name
155
     * @return int the number of indexes dropped. False if there was a datbase error
156
     */
157
    public function dropIndex($table_name, $index_name)
158
    {
159
        if (apply_filters('FHEE__EEH_Activation__drop_index__short_circuit', false)) {
160
            return 0;
161
        }
162
        global $wpdb;
163
        $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
164
        $index_exists_query = "SHOW INDEX FROM {$table_name} WHERE key_name = '{$index_name}'";
165
        if ($this->getTableAnalysis()->tableExists($table_name)
166
            && $wpdb->get_var($index_exists_query)
167
               === $table_name // using get_var with the $index_exists_query returns the table's name
168
        ) {
169
            return $wpdb->query("ALTER TABLE {$table_name} DROP INDEX {$index_name}");
170
        }
171
        return 0;
172
    }
173
174
175
    /**
176
     * Just creates the requested table. $table_name can
177
     * optionally start with $wpdb->prefix or not
178
     *
179
     * @param string $table_name
180
     * @param string $create_sql defining the table's columns and indexes
181
     * @param string $engine     (no need to specify "ENGINE=", that's implied)
182
     * @return void
183
     * @throws \EE_Error
184
     */
185
    public function createTable($table_name, $create_sql, $engine = 'MyISAM')
186
    {
187
        // does $sql contain valid column information? ( LPT: https://regex101.com/ is great for working out regex patterns )
188
        if (preg_match('((((.*?))(,\s))+)', $create_sql, $valid_column_data)) {
189
            $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
190
            /** @var \wpdb $wpdb */
191
            global $wpdb;
192
            $SQL = "CREATE TABLE {$table_name} ( {$create_sql} ) ENGINE={$engine} " . $wpdb->get_charset_collate();
193
194
            // get $wpdb to echo errors, but buffer them. This way at least WE know an error
195
            // happened. And then we can choose to tell the end user
196
            $old_show_errors_policy = $wpdb->show_errors(true);
197
            $old_error_suppression_policy = $wpdb->suppress_errors(false);
198
            ob_start();
199
            dbDelta($SQL);
200
            $output = ob_get_contents();
201
            ob_end_clean();
202
            $wpdb->show_errors($old_show_errors_policy);
203
            $wpdb->suppress_errors($old_error_suppression_policy);
204
            if (! empty($output)) {
205
                throw new \EE_Error($output);
206
            }
207
        } else {
208
            throw new \EE_Error(
209
                sprintf(
210
                    __(
211
                        'The following table creation SQL does not contain valid information about the table columns: %1$s %2$s',
212
                        'event_espresso'
213
                    ),
214
                    '<br />',
215
                    $create_sql
216
                )
217
            );
218
        }
219
    }
220
221
222
    /**
223
     * Drops the specified index if it's size differs from $desired_index_size.
224
     * WordPress' dbdelta method doesn't automatically change index sizes, so this
225
     * method can be used to only drop the index if needed, and afterwards dbdelta can be used as normal.
226
     * If the table doesn't exist, or it exists but the index does not, or returns false
227
     *
228
     * @param string     $table_name
229
     * @param string     $index_name
230
     * @param string     $column_name        if none is provided, we assume the column name matches the index (often
231
     *                                       true in EE)
232
     * @param string|int $desired_index_size defaults to TableAnalysis::index_col_size, the max for utf8mb4.
233
     * @return bool whether an index was dropped or not
234
     * @throws /EE_Error if table analysis object isn't defined
235
     */
236
    public function dropIndexIfSizeNot(
237
        $table_name,
238
        $index_name,
239
        $column_name = null,
240
        $desired_index_size = TableAnalysis::INDEX_COLUMN_SIZE
241
    ) {
242
        if ($column_name === null) {
243
            $column_name = $index_name;
244
        }
245
        if (! $this->getTableAnalysis()->tableExists($table_name)) {
246
            return false;
247
        }
248
        $index_entries = $this->getTableAnalysis()->showIndexes($table_name, $index_name);
249
        if (empty($index_entries)) {
250
            return false;
251
        }
252
        foreach ($index_entries as $index_entry) {
253
            if ($column_name === $index_entry->Column_name
254
                && (string) $desired_index_size !== $index_entry->Sub_part) {
255
                return $this->dropIndex($table_name, $index_name);
256
            }
257
        }
258
        return false;
259
    }
260
}
261