Completed
Branch BUG-9042-ics-organizer-email (0077fa)
by
unknown
26:47 queued 15:14
created

TableManager::dropIndexIfSizeNot()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 11
nc 8
nop 4
dl 0
loc 17
rs 8.8571
c 0
b 0
f 0
1
<?php
2
namespace EventEspresso\core\services\database;
3
4
defined('EVENT_ESPRESSO_VERSION') || exit('No direct script access allowed');
5
6
7
8
/**
9
 * Class TableManager
10
 * For performing mysql database table schema manipulation
11
 *
12
 * @package               Event Espresso
13
 * @subpackage
14
 * @author                Mike Nelson
15
 * @since                 $VID:$
16
 */
17
class TableManager extends \EE_Base
18
{
19
20
    /**
21
     * @var TableAnalysis $table_analysis
22
     */
23
    private $table_analysis;
24
25
26
27
    /**
28
     * TableManager constructor.
29
     *
30
     * @param TableAnalysis $TableAnalysis
31
     */
32
    public function __construct(TableAnalysis $TableAnalysis)
33
    {
34
        $this->table_analysis = $TableAnalysis;
35
    }
36
37
38
39
    /**
40
     * Gets the injected table analyzer, or throws an exception
41
     *
42
     * @return TableAnalysis
43
     * @throws \EE_Error
44
     */
45 View Code Duplication
    protected function getTableAnalysis()
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...
46
    {
47
        if ($this->table_analysis instanceof TableAnalysis) {
48
            return $this->table_analysis;
49
        } else {
50
            throw new \EE_Error(
51
                sprintf(
52
                    __('Table analysis class on class %1$s is not set properly.', 'event_espresso'),
53
                    get_class($this)
54
                )
55
            );
56
        }
57
    }
58
59
60
61
    /**
62
     * @param string $table_name which can optionally start with $wpdb->prefix or not
63
     * @param string $column_name
64
     * @param string $column_info
65
     * @return bool|false|int
66
     */
67
    public function addColumn($table_name, $column_name, $column_info = 'INT UNSIGNED NOT NULL')
68
    {
69
        if (apply_filters('FHEE__EEH_Activation__add_column_if_it_doesnt_exist__short_circuit', false)) {
70
            return false;
71
        }
72
        global $wpdb;
73
        $full_table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
74
        $columns = $this->getTableColumns($table_name);
75
        if ( ! in_array($column_name, $columns)) {
76
            $alter_query = "ALTER TABLE {$full_table_name} ADD {$column_name} {$column_info}";
77
            return $wpdb->query($alter_query);
78
        }
79
        return true;
80
    }
81
82
83
84
    /**
85
     * Gets the name of all columns on the  table. $table_name can
86
     * optionally start with $wpdb->prefix or not
87
     *
88
     * @global \wpdb $wpdb
89
     * @param string $table_name
90
     * @return array
91
     */
92
    public function getTableColumns($table_name)
93
    {
94
        global $wpdb;
95
        $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
96
        $field_array = array();
97
        if ( ! empty($table_name)) {
98
            $columns = $wpdb->get_results("SHOW COLUMNS FROM {$table_name} ");
99
            if ($columns !== false) {
100
                foreach ($columns as $column) {
101
                    $field_array[] = $column->Field;
102
                }
103
            }
104
        }
105
        return $field_array;
106
    }
107
108
109
110
    /**
111
     * Drops the specified table from the database. $table_name can
112
     * optionally start with $wpdb->prefix or not
113
     *
114
     * @global \wpdb $wpdb
115
     * @param string $table_name
116
     * @return int
117
     */
118
    public function dropTable($table_name)
119
    {
120
        global $wpdb;
121
        if ($this->getTableAnalysis()->tableExists($table_name)) {
122
            $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
123
            return $wpdb->query("DROP TABLE IF EXISTS {$table_name}");
124
        }
125
        return 0;
126
    }
127
128
129
130
    /**
131
     * Drops all the tables mentioned in a single MYSQL query. Double-checks
132
     * each table name provided has a wpdb prefix attached, and that it exists.
133
     * Returns the list actually deleted
134
     *
135
     * @global WPDB $wpdb
136
     * @param array $table_names
137
     * @return array of table names which we deleted
138
     */
139
    public function dropTables($table_names)
140
    {
141
        $tables_to_delete = array();
142
        foreach ($table_names as $table_name) {
143
            $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
144
            if ($this->getTableAnalysis()->tableExists($table_name)) {
145
                $tables_to_delete[] = $table_name;
146
            }
147
        }
148
        if( ! empty( $tables_to_delete ) ) {
149
            global $wpdb;
150
            $wpdb->query('DROP TABLE ' . implode(', ', $tables_to_delete));
151
        }
152
        return $tables_to_delete;
153
    }
154
155
156
157
    /**
158
     * Drops the specified index from the specified table. $table_name can
159
     * optionally start with $wpdb->prefix or not
160
161
     *
162
*@global \wpdb       $wpdb
163
     * @param string $table_name
164
     * @param string $index_name
165
     * @return int
166
     */
167
    public function dropIndex($table_name, $index_name)
168
    {
169
        if (apply_filters('FHEE__EEH_Activation__drop_index__short_circuit', false)) {
170
            return false;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return false; (false) is incompatible with the return type documented by EventEspresso\core\servi...TableManager::dropIndex of type integer.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

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

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
171
        }
172
        global $wpdb;
173
        $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
174
        $index_exists_query = "SHOW INDEX FROM {$table_name} WHERE key_name = '{$index_name}'";
175
        if (
176
            $this->getTableAnalysis()->tableExists($table_name)
177
            && $wpdb->get_var($index_exists_query)
178
               === $table_name //using get_var with the $index_exists_query returns the table's name
179
        ) {
180
            return $wpdb->query("ALTER TABLE {$table_name} DROP INDEX {$index_name}");
181
        }
182
        return 0;
183
    }
184
185
186
187
    /**
188
     * Just creates the requested table. $table_name can
189
     * optionally start with $wpdb->prefix or not
190
191
     *
192
*@param string       $table_name
193
     * @param string $create_sql defining the table's columns and indexes
194
     * @param string $engine     (no need to specify "ENGINE=", that's implied)
195
     * @return void
196
     * @throws \EE_Error
197
     */
198
    public function createTable($table_name, $create_sql, $engine = 'MyISAM')
199
    {
200
        // does $sql contain valid column information? ( LPT: https://regex101.com/ is great for working out regex patterns )
201
        if (preg_match('((((.*?))(,\s))+)', $create_sql, $valid_column_data)) {
202
            $table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix($table_name);
203
            /** @var \wpdb $wpdb */
204
            global $wpdb;
205
            $SQL = "CREATE TABLE {$table_name} ( {$create_sql} ) ENGINE={$engine} " . $wpdb->get_charset_collate();
206
207
            //get $wpdb to echo errors, but buffer them. This way at least WE know an error
208
            //happened. And then we can choose to tell the end user
209
            $old_show_errors_policy = $wpdb->show_errors(true);
210
            $old_error_suppression_policy = $wpdb->suppress_errors(false);
211
            ob_start();
212
            dbDelta($SQL);
213
            $output = ob_get_contents();
214
            ob_end_clean();
215
            $wpdb->show_errors($old_show_errors_policy);
216
            $wpdb->suppress_errors($old_error_suppression_policy);
217
            if ( ! empty($output)) {
218
                throw new \EE_Error($output);
219
            }
220
        } else {
221
            throw new \EE_Error(
222
                sprintf(
223
                    __('The following table creation SQL does not contain valid information about the table columns: %1$s %2$s',
224
                        'event_espresso'),
225
                    '<br />',
226
                    $create_sql
227
                )
228
            );
229
        }
230
    }
231
232
233
234
    /**
235
     * Drops the specified index if it's size differs from $desired_index_size.
236
     * WordPress' dbdelta method doesn't automatically change index sizes, so this
237
     * method can be used to only drop the index if needed, and afterwards dbdelta can be used as normal.
238
     *
239
*@param string $table_name
240
     * @param string $index_name
241
     * @param string $column_name if none is provided, we assume the column name matches the index (often true in EE)
242
     * @param string|int $desired_index_size defaults to 191, the max for utf8mb4.
243
     *                   See https://events.codebasehq.com/redirect?https://make.wordpress.org/core/2015/04/02/the-utf8mb4-upgrade/
244
     * @return bool
245
     * @throws /EE_Error if table analysis object isn't defined
246
     */
247
    public function dropIndexIfSizeNot($table_name, $index_name, $column_name = null, $desired_index_size = 191)
248
    {
249
        if($column_name === null){
250
            $column_name = $index_name;
251
        }
252
        $index_entries = $this->getTableAnalysis()->showIndexes($table_name,$index_name);
253
        if(empty($index_entries)){
254
            return false;
255
        }
256
        foreach($index_entries as $index_entry){
257
            if( $column_name === $index_entry->Column_name
258
                && (string)$desired_index_size !== $index_entry->Sub_part){
259
                return $this->dropIndex($table_name,$index_name);
260
            }
261
        }
262
        return false;
263
    }
264
265
}
266