1
|
|
|
<?php |
2
|
|
|
namespace EventEspresso\core\services\database; |
3
|
|
|
|
4
|
|
|
defined( 'EVENT_ESPRESSO_VERSION') || exit('No direct script access allowed'); |
5
|
|
|
/** |
6
|
|
|
* |
7
|
|
|
* Class TableManager |
8
|
|
|
* |
9
|
|
|
* For performing mysql database table schema manipulation |
10
|
|
|
* |
11
|
|
|
* @package Event Espresso |
12
|
|
|
* @subpackage |
13
|
|
|
* @author Mike Nelson |
14
|
|
|
* @since $VID:$ |
15
|
|
|
* |
16
|
|
|
*/ |
17
|
|
|
class TableManager extends \EE_Base { |
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @var TableAnalysis $table_analysis |
21
|
|
|
*/ |
22
|
|
|
private $table_analysis; |
23
|
|
|
|
24
|
|
|
|
25
|
|
|
|
26
|
|
|
/** |
27
|
|
|
* TableManager constructor. |
28
|
|
|
* |
29
|
|
|
* @param TableAnalysis $TableAnalysis |
30
|
|
|
*/ |
31
|
|
|
public function __construct( TableAnalysis $TableAnalysis ) { |
32
|
|
|
$this->table_analysis = $TableAnalysis; |
33
|
|
|
} |
34
|
|
|
|
35
|
|
|
/** |
36
|
|
|
* Gets the injected table analyzer, or throws an exception |
37
|
|
|
* @return TableAnalysis |
38
|
|
|
* @throws \EE_Error |
39
|
|
|
*/ |
40
|
|
View Code Duplication |
protected function getTableAnalysis() { |
|
|
|
|
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
|
|
|
/** |
56
|
|
|
* @param string $table_name which can optionally start with $wpdb->prefix or not |
57
|
|
|
* @param string $column_name |
58
|
|
|
* @param string $column_info |
59
|
|
|
* @return bool|false|int |
60
|
|
|
*/ |
61
|
|
|
public function addColumn( $table_name, $column_name, $column_info='INT UNSIGNED NOT NULL' ) |
62
|
|
|
{ |
63
|
|
|
if( apply_filters( 'FHEE__EEH_Activation__add_column_if_it_doesnt_exist__short_circuit', FALSE ) ){ |
64
|
|
|
return FALSE; |
65
|
|
|
} |
66
|
|
|
global $wpdb; |
67
|
|
|
$full_table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix( $table_name ); |
68
|
|
|
$columns = $this->getTableColumns($table_name); |
69
|
|
|
if( !in_array( $column_name, $columns)){ |
70
|
|
|
$alter_query="ALTER TABLE $full_table_name ADD $column_name $column_info"; |
71
|
|
|
return $wpdb->query($alter_query); |
72
|
|
|
} |
73
|
|
|
return TRUE; |
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
|
|
|
* @global \wpdb $wpdb |
80
|
|
|
* @param string $table_name |
81
|
|
|
* @return array |
82
|
|
|
*/ |
83
|
|
|
public function getTableColumns( $table_name ) |
84
|
|
|
{ |
85
|
|
|
global $wpdb; |
86
|
|
|
$table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix( $table_name ); |
87
|
|
|
$fieldArray = array(); |
88
|
|
|
if ( ! empty( $table_name )) { |
89
|
|
|
$columns = $wpdb->get_results("SHOW COLUMNS FROM $table_name "); |
90
|
|
|
if ($columns !== FALSE) { |
91
|
|
|
foreach( $columns as $column ){ |
92
|
|
|
$fieldArray[] = $column->Field; |
93
|
|
|
} |
94
|
|
|
} |
95
|
|
|
} |
96
|
|
|
return $fieldArray; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Drops the specified table from the database. $table_name can |
101
|
|
|
* optionally start with $wpdb->prefix or not |
102
|
|
|
* |
103
|
|
|
* @global \wpdb $wpdb |
104
|
|
|
* @param string $table_name |
105
|
|
|
* @return int |
106
|
|
|
*/ |
107
|
|
|
public function dropTable( $table_name ) |
108
|
|
|
{ |
109
|
|
|
global $wpdb; |
110
|
|
|
if ( $this->getTableAnalysis()->tableExists( $table_name ) ) { |
111
|
|
|
$table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix( $table_name ); |
112
|
|
|
return $wpdb->query( "DROP TABLE IF EXISTS $table_name" ); |
113
|
|
|
} |
114
|
|
|
return 0; |
115
|
|
|
} |
116
|
|
|
|
117
|
|
|
/** |
118
|
|
|
* Drops all the tables mentioned in a single MYSQL query. Double-checks |
119
|
|
|
* each table name provided has a wpdb prefix attached, and that it exists. |
120
|
|
|
* Returns the list actually deleted |
121
|
|
|
* @global WPDB $wpdb |
122
|
|
|
* @param array $table_names |
123
|
|
|
* @return array of table names which we deleted |
124
|
|
|
*/ |
125
|
|
|
public function dropTables( $table_names ) |
126
|
|
|
{ |
127
|
|
|
$tables_to_delete = array(); |
128
|
|
|
foreach( $table_names as $table_name ) { |
129
|
|
|
$table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix( $table_name ); |
130
|
|
|
if( $this->getTableAnalysis()->tableExists( $table_name ) ) { |
131
|
|
|
$tables_to_delete[] = $table_name; |
132
|
|
|
} |
133
|
|
|
} |
134
|
|
|
global $wpdb; |
135
|
|
|
$wpdb->query( 'DROP TABLE ' . implode( ', ', $tables_to_delete ) ); |
136
|
|
|
return $tables_to_delete; |
137
|
|
|
} |
138
|
|
|
|
139
|
|
|
/** |
140
|
|
|
* Drops the specified index from the specified table. $table_name can |
141
|
|
|
* optionally start with $wpdb->prefix or not |
142
|
|
|
* |
143
|
|
|
* @global \wpdb $wpdb |
144
|
|
|
* @param string $table_name |
145
|
|
|
* @param string $indexName |
146
|
|
|
* @return int |
147
|
|
|
*/ |
148
|
|
|
public function dropIndex( $table_name, $indexName ) |
149
|
|
|
{ |
150
|
|
|
if( apply_filters( 'FHEE__EEH_Activation__drop_index__short_circuit', FALSE ) ){ |
151
|
|
|
return FALSE; |
|
|
|
|
152
|
|
|
} |
153
|
|
|
global $wpdb; |
154
|
|
|
$table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix( $table_name ); |
155
|
|
|
$index_exists_query = "SHOW INDEX FROM $table_name WHERE Key_name = '$indexName'"; |
156
|
|
|
if ( |
157
|
|
|
$this->getTableAnalysis()->tableExists( $table_name ) |
158
|
|
|
&& $wpdb->get_var( $index_exists_query ) === $table_name //using get_var with the $index_exists_query returns the table's name |
159
|
|
|
) { |
160
|
|
|
return $wpdb->query( "ALTER TABLE $table_name DROP INDEX $indexName" ); |
161
|
|
|
} |
162
|
|
|
return 0; |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
/** |
166
|
|
|
* Just creates the requested table. $table_name can |
167
|
|
|
* optionally start with $wpdb->prefix or not |
168
|
|
|
* @param string $table_name |
169
|
|
|
* @param string $createSql defining the table's columns and indexes |
170
|
|
|
* @param string $engine (no need to specify "ENGINE=", that's implied) |
171
|
|
|
* @return void |
172
|
|
|
* @throws \EE_Error |
173
|
|
|
*/ |
174
|
|
|
public function createTable( $table_name, $createSql, $engine = 'MyISAM' ) |
175
|
|
|
{ |
176
|
|
|
// does $sql contain valid column information? ( LPT: https://regex101.com/ is great for working out regex patterns ) |
177
|
|
|
if ( preg_match( '((((.*?))(,\s))+)', $createSql, $valid_column_data ) ) { |
178
|
|
|
$table_name = $this->getTableAnalysis()->ensureTableNameHasPrefix( $table_name ); |
179
|
|
|
$SQL = "CREATE TABLE $table_name ( $createSql ) ENGINE=$engine DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;"; |
180
|
|
|
/** @var \wpdb $wpdb */ |
181
|
|
|
global $wpdb; |
182
|
|
|
//get $wpdb to echo errors, but buffer them. This way at least WE know an error |
183
|
|
|
//happened. And then we can choose to tell the end user |
184
|
|
|
$old_show_errors_policy = $wpdb->show_errors( TRUE ); |
185
|
|
|
$old_error_suppression_policy = $wpdb->suppress_errors( FALSE ); |
186
|
|
|
ob_start(); |
187
|
|
|
dbDelta( $SQL ); |
188
|
|
|
$output = ob_get_contents(); |
189
|
|
|
ob_end_clean(); |
190
|
|
|
$wpdb->show_errors( $old_show_errors_policy ); |
191
|
|
|
$wpdb->suppress_errors( $old_error_suppression_policy ); |
192
|
|
|
if( ! empty( $output ) ){ |
193
|
|
|
throw new \EE_Error( $output ); |
194
|
|
|
} |
195
|
|
|
} else { |
196
|
|
|
throw new \EE_Error( |
197
|
|
|
sprintf( |
198
|
|
|
__( 'The following table creation SQL does not contain valid information about the table columns: %1$s %2$s', 'event_espresso' ), |
199
|
|
|
'<br />', |
200
|
|
|
$createSql |
201
|
|
|
) |
202
|
|
|
); |
203
|
|
|
} |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
} |
207
|
|
|
|
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.