PluginProbe
User Access Manager / 2.1.9
User Access Manager v2.1.9
2.3.20 2.3.19 2.3.18 2.3.17 2.3.16 2.3.15 2.3.14 2.3.13 trunk 0.6 0.6.1 0.6.2 0.7 0.7 Beta 0.7.0.1 0.8 0.8.0.1 0.8.0.2 0.9 0.9.1 0.9.1.1 0.9.1.2 0.9.1.3 0.9.1.4 1.0 All 136 releases
user-access-manager / src / Setup / Database / DatabaseHandler.php

DatabaseHandler.php in User Access Manager 2.1.9, at src/Setup/Database/DatabaseHandler.php

546 lines 15.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * DatabaseHandler.php
4 *
5 * The DatabaseHandler class file.
6 *
7 * PHP versions 5
8 *
9 * @author Alexander Schneider <alexanderschneider85@gmail.com>
10 * @copyright 2008-2017 Alexander Schneider
11 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU General Public License, version 2
12 * @version SVN: $id$
13 * @link http://wordpress.org/extend/plugins/user-access-manager/
14 */
15 namespace UserAccessManager\Setup\Database;
16
17 use UserAccessManager\Config\WordpressConfig;
18 use UserAccessManager\Database\Database;
19 use UserAccessManager\Setup\Update\UpdateFactory;
20 use UserAccessManager\Setup\Update\UpdateInterface;
21 use UserAccessManager\UserAccessManager;
22 use UserAccessManager\Wrapper\Wordpress;
23
24 /**
25 * Class DatabaseHandler
26 *
27 * @package UserAccessManager\Setup\Database
28 */
29 class DatabaseHandler
30 {
31 const MISSING_TABLES = 'MISSING_TABLE';
32 const MISSING_COLUMNS = 'MISSING_COLUMNS';
33 const MODIFIED_COLUMNS = 'MODIFIED_COLUMNS';
34 const EXTRA_COLUMNS = 'EXTRA_COLUMNS';
35
36 /**
37 * @var Wordpress
38 */
39 private $wordpress;
40
41 /**
42 * @var Database
43 */
44 private $database;
45
46 /**
47 * @var DatabaseObjectFactory
48 */
49 private $databaseObjectFactory;
50
51 /**
52 * @var UpdateFactory
53 */
54 private $updateFactory;
55
56 /**
57 * DatabaseHandler constructor.
58 *
59 * @param Wordpress $wordpress
60 * @param Database $database
61 * @param DatabaseObjectFactory $databaseObjectFactory
62 * @param UpdateFactory $updateFactory
63 */
64 public function __construct(
65 Wordpress $wordpress,
66 Database $database,
67 DatabaseObjectFactory $databaseObjectFactory,
68 UpdateFactory $updateFactory
69 ) {
70 $this->wordpress = $wordpress;
71 $this->database = $database;
72 $this->databaseObjectFactory = $databaseObjectFactory;
73 $this->updateFactory = $updateFactory;
74 }
75
76 /**
77 * Checks if the table exists.
78 *
79 * @param string $table
80 *
81 * @return bool
82 */
83 private function tableExists($table)
84 {
85 $dbTable = $this->database->getVariable("SHOW TABLES LIKE '{$table}'");
86
87 return ($table === $dbTable);
88 }
89
90 /**
91 * Adds a table.
92 *
93 * @param Table $table
94 */
95 private function addTable(Table $table)
96 {
97 $this->database->dbDelta((string)$table);
98 }
99
100 /**
101 * Returns all tables.
102 *
103 * @return Table[]
104 */
105 private function getTables()
106 {
107 $charsetCollate = $this->database->getCharset();
108 $tables = [];
109
110 $tables[] = $this->databaseObjectFactory->createTable(
111 $this->database->getUserGroupTable(),
112 $charsetCollate,
113 [
114 $this->databaseObjectFactory->createColumn('ID', 'INT(11)', false, null, true, true),
115 $this->databaseObjectFactory->createColumn('groupname', 'TINYTEXT'),
116 $this->databaseObjectFactory->createColumn('groupdesc', 'TEXT'),
117 $this->databaseObjectFactory->createColumn('read_access', 'TINYTEXT'),
118 $this->databaseObjectFactory->createColumn('write_access', 'TINYTEXT'),
119 $this->databaseObjectFactory->createColumn('ip_range', 'MEDIUMTEXT', true)
120 ]
121 );
122
123 $tables[] = $this->databaseObjectFactory->createTable(
124 $this->database->getUserGroupToObjectTable(),
125 $charsetCollate,
126 [
127 $this->databaseObjectFactory->createColumn('object_id', 'VARCHAR(32)', false, null, true),
128 $this->databaseObjectFactory->createColumn('general_object_type', 'VARCHAR(64)'),
129 $this->databaseObjectFactory->createColumn('object_type', 'VARCHAR(32)', false, null, true),
130 $this->databaseObjectFactory->createColumn('group_id', 'VARCHAR(32)', false, null, true),
131 $this->databaseObjectFactory->createColumn('group_type', 'VARCHAR(32)', false, null, true),
132 $this->databaseObjectFactory->createColumn('from_date', 'DATETIME', true),
133 $this->databaseObjectFactory->createColumn('to_date', 'DATETIME', true)
134 ]
135 );
136
137 return $tables;
138 }
139
140 /**
141 * Adds the tables to the database.
142 */
143 public function install()
144 {
145 foreach ($this->getTables() as $table) {
146 if ($this->tableExists($table->getName()) === false) {
147 $this->addTable($table);
148 }
149 }
150
151 $this->wordpress->addOption('uam_db_version', UserAccessManager::DB_VERSION);
152 }
153
154 /**
155 * Returns the existing columns for a table.
156 *
157 * @param Table $table
158 *
159 * @return Column[]
160 */
161 private function getExistingColumns(Table $table)
162 {
163 $query = "SHOW COLUMNS FROM `{$table->getName()}`;";
164 $existingRawColumns = (array)$this->database->getResults($query);
165 $existingColumns = [];
166
167 foreach ($existingRawColumns as $existingRawColumn) {
168 $existingColumns[$existingRawColumn->Field] = $this->databaseObjectFactory->createColumn(
169 $existingRawColumn->Field,
170 strtoupper($existingRawColumn->Type),
171 $existingRawColumn->Null === 'YES',
172 $existingRawColumn->Default,
173 $existingRawColumn->Key === 'PRI',
174 $existingRawColumn->Extra === 'auto_increment'
175 );
176 }
177
178 return $existingColumns;
179 }
180
181 /**
182 * Add corrupted columns to the information array if the are some.
183 *
184 * @param Table $table
185 * @param array $information
186 */
187 private function addCorruptedRows(Table $table, array &$information)
188 {
189 $existingColumns = $this->getExistingColumns($table);
190
191 foreach ($table->getColumns() as $column) {
192 if (isset($existingColumns[$column->getName()]) === false) {
193 $information[self::MISSING_COLUMNS][] = [$table, $column];
194 continue;
195 }
196
197 $existingColumn = $existingColumns[$column->getName()];
198 unset($existingColumns[$column->getName()]);
199
200 if ((string)$column !== (string)$existingColumn) {
201 $information[self::MODIFIED_COLUMNS][] = [$table, $column];
202 continue;
203 }
204 }
205
206 foreach ($existingColumns as $existingColumn) {
207 $information[self::EXTRA_COLUMNS][] = [$table, $existingColumn];
208 }
209 }
210
211 /**
212 * Returns corrupted database information.
213 *
214 * @return array
215 */
216 public function getCorruptedDatabaseInformation()
217 {
218 $information = [
219 self::MISSING_TABLES => [],
220 self::MISSING_COLUMNS => [],
221 self::MODIFIED_COLUMNS => [],
222 self::EXTRA_COLUMNS => []
223 ];
224
225 foreach ($this->getTables() as $table) {
226 if ($this->tableExists($table->getName()) === false) {
227 $information[self::MISSING_TABLES][] = $table;
228 continue;
229 }
230
231 $this->addCorruptedRows($table, $information);
232 }
233
234 return $information;
235 }
236
237 /**
238 * Adds a new column.
239 *
240 * @param Table $table
241 * @param Column $column
242 *
243 * @return bool
244 */
245 private function addColumn(Table $table, Column $column)
246 {
247 return $this->database->query("ALTER TABLE `{$table->getName()}` ADD $column;") !== false;
248 }
249
250 /**
251 * Modify an existing column.
252 *
253 * @param Table $table
254 * @param Column $column
255 *
256 * @return bool
257 */
258 private function modifyColumn(Table $table, Column $column)
259 {
260 return $this->database->query("ALTER TABLE `{$table->getName()}` MODIFY $column;") !== false;
261 }
262
263 /**
264 * Drops an existing column.
265 *
266 * @param Table $table
267 * @param Column $column
268 *
269 * @return bool
270 */
271 private function dropColumn(Table $table, Column $column)
272 {
273 return $this->database->query("ALTER TABLE `{$table->getName()}` DROP `{$column->getName()}`;") !== false;
274 }
275
276 /**
277 * Repairs a corrupt database.
278 *
279 * @param array $information
280 *
281 * @return bool
282 */
283 public function repairDatabase(array $information = [])
284 {
285 $success = true;
286 $information = ($information === []) ? $this->getCorruptedDatabaseInformation() : $information;
287
288 foreach ($information[self::MISSING_TABLES] as $table) {
289 $this->addTable($table);
290 }
291
292 foreach ($information[self::MISSING_COLUMNS] as $columnInformation) {
293 $success = $success && $this->addColumn($columnInformation[0], $columnInformation[1]);
294 }
295
296 foreach ($information[self::MODIFIED_COLUMNS] as $columnInformation) {
297 $success = $success && $this->modifyColumn($columnInformation[0], $columnInformation[1]);
298 }
299
300 foreach ($information[self::EXTRA_COLUMNS] as $columnInformation) {
301 $success = $success && $this->dropColumn($columnInformation[0], $columnInformation[1]);
302 }
303
304 return $success;
305 }
306
307 /**
308 * Returns all sites where the user access manager is active.
309 *
310 * @return array
311 */
312 private function getActivePluginSites()
313 {
314 $currentBlogId = $this->database->getCurrentBlogId();
315 $activeSites = [];
316
317 foreach ($this->wordpress->getSites() as $site) {
318 $this->wordpress->switchToBlog($site->blog_id);
319 $plugins = (array)$this->wordpress->getOption('active_plugins', []);
320 $pluginsMap = array_flip($plugins);
321
322 if (isset($pluginsMap['user-access-manager/user-access-manager.php']) === true) {
323 $activeSites[$site->blog_id] = $site->blog_id;
324 }
325 }
326
327 $this->wordpress->switchToBlog($currentBlogId);
328
329 return $activeSites;
330 }
331
332 /**
333 * Checks if a database update is necessary.
334 *
335 * @return bool
336 */
337 public function isDatabaseUpdateNecessary()
338 {
339 if ($this->wordpress->isSuperAdmin() === true) {
340 foreach ($this->getActivePluginSites() as $siteId) {
341 $table = $this->database->getBlogPrefix($siteId).'options';
342 $select = "SELECT option_value FROM {$table} WHERE option_name = '%s' LIMIT 1";
343 $select = $this->database->prepare($select, 'uam_db_version');
344 $currentDbVersion = $this->database->getVariable($select);
345
346 if ($currentDbVersion !== null
347 && version_compare($currentDbVersion, UserAccessManager::DB_VERSION, '<') === true
348 ) {
349 return true;
350 }
351 }
352 }
353
354 $currentDbVersion = $this->wordpress->getOption('uam_db_version');
355 return version_compare($currentDbVersion, UserAccessManager::DB_VERSION, '<');
356 }
357
358 /**
359 * Creates a database backup.
360 *
361 * @return bool
362 */
363 public function backupDatabase()
364 {
365 $currentDbVersion = $this->wordpress->getOption('uam_db_version');
366
367 if (empty($currentDbVersion) === true
368 || version_compare($currentDbVersion, '1.2', '<') === true
369 ) {
370 return false;
371 }
372
373 $tables = [
374 $this->database->getUserGroupTable(),
375 $this->database->getUserGroupToObjectTable()
376 ];
377
378 $currentDbVersion = str_replace('.', '-', $currentDbVersion);
379 $success = true;
380
381 foreach ($tables as $table) {
382 $createQuery = "CREATE TABLE `{$table}_{$currentDbVersion}` LIKE `{$table}`";
383 $success = $success && ($this->database->query($createQuery) !== false);
384 $insertQuery = "INSERT `{$table}_{$currentDbVersion}` SELECT * FROM `{$table}`";
385 $success = $success && ($this->database->query($insertQuery) !== false);
386 }
387
388 return $success;
389 }
390
391 /**
392 * Returns the version for which a backup was created.
393 *
394 * @return array
395 */
396 public function getBackups()
397 {
398 $versions = [];
399 $tables = (array)$this->database->getColumn(
400 "SHOW TABLES LIKE '{$this->database->getPrefix()}uam_%'"
401 );
402
403 foreach ($tables as $table) {
404 if (preg_match('/.*\_([0-9\-]+)/i', $table, $matches) === 1) {
405 $version = str_replace('-', '.', $matches[1]);
406 $versions[$version] = $version;
407 }
408 }
409
410 return $versions;
411 }
412
413 /**
414 * Returns the backup tables for the given version.
415 *
416 * @param string $version
417 *
418 * @return array
419 */
420 private function getBackupTables($version)
421 {
422 $backupTables = [];
423 $tables = [
424 $this->database->getUserGroupTable(),
425 $this->database->getUserGroupToObjectTable()
426 ];
427
428 $versionForDb = str_replace('.', '-', $version);
429
430 foreach ($tables as $table) {
431 $backupTable = (string)$this->database->getVariable(
432 "SHOW TABLES LIKE '{$table}_{$versionForDb}'"
433 );
434
435 if ($backupTable !== '') {
436 $backupTables[$table] = $backupTable;
437 }
438 }
439
440 return $backupTables;
441 }
442
443 /**
444 * Reverts the database to the given version.
445 *
446 * @param string $version
447 *
448 * @return bool
449 */
450 public function revertDatabase($version)
451 {
452 $success = true;
453 $tables = $this->getBackupTables($version);
454
455 foreach ($tables as $table => $backupTable) {
456 $dropQuery = "DROP TABLE IF EXISTS `{$table}`";
457 $success = $success && ($this->database->query($dropQuery) !== false);
458 $renameQuery = "RENAME TABLE `{$backupTable}` TO `{$table}`";
459 $success = $success && ($this->database->query($renameQuery) !== false);
460 }
461
462 if ($success === true) {
463 $this->wordpress->updateOption('uam_db_version', $version);
464 }
465
466 return $success;
467 }
468
469 /**
470 * Deletes the given database backup.
471 *
472 * @param string $version
473 *
474 * @return bool
475 */
476 public function deleteBackup($version)
477 {
478 $success = true;
479 $tables = $this->getBackupTables($version);
480
481 foreach ($tables as $table => $backupTable) {
482 $dropQuery = "DROP TABLE IF EXISTS `{$backupTable}`";
483 $success = $success && ($this->database->query($dropQuery) !== false);
484 }
485
486 return $success;
487 }
488
489 /**
490 * Returns the ordered updates.
491 *
492 * @return UpdateInterface[]
493 */
494 private function getOrderedDatabaseUpdates()
495 {
496 $rawUpdates = $this->updateFactory->getDatabaseUpdates();
497 $updates = [];
498
499 foreach ($rawUpdates as $rawUpdate) {
500 $updates[$rawUpdate->getVersion()] = $rawUpdate;
501 }
502
503 uksort($updates, 'version_compare');
504 return $updates;
505 }
506
507 /**
508 * Updates the database.
509 *
510 * @return bool
511 */
512 public function updateDatabase()
513 {
514 $currentDbVersion = $this->wordpress->getOption('uam_db_version');
515
516 if (empty($currentDbVersion) === true) {
517 return false;
518 }
519
520 $success = true;
521
522 foreach ($this->getOrderedDatabaseUpdates() as $orderedUpdate) {
523 if (version_compare($currentDbVersion, $orderedUpdate->getVersion(), '<') === true) {
524 $success = $success && $orderedUpdate->update();
525 }
526 }
527
528 if ($success === true) {
529 $this->wordpress->updateOption('uam_db_version', UserAccessManager::DB_VERSION);
530 }
531
532 return $success;
533 }
534
535 /**
536 * Removes the tables.
537 */
538 public function removeTables()
539 {
540 foreach ($this->getTables() as $table) {
541 $dropQuery = "DROP TABLE IF EXISTS `{$table->getName()}`";
542 $this->database->query($dropQuery);
543 }
544 }
545 }
546