PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backup, Restore, Migration & Clone / trunk
WP STAGING – WordPress Backup, Restore, Migration & Clone vtrunk
4.9.1 4.9.0 4.8.1 trunk 3.0.0 3.0.1 3.0.2 3.0.3 3.0.4 3.0.5 3.0.6 3.1.0 3.1.1 3.1.2 3.1.3 3.1.4 3.10.0 3.2.0 3.3.1 3.3.2 3.3.3 3.4.1 3.4.3 3.5.0 3.6.0 3.7.1 3.8.0 3.8.1 3.8.2 3.8.3 3.8.4 3.8.5 3.8.6 3.8.7 3.9.0 3.9.1 3.9.2 3.9.3 3.9.4 4.0.0 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.2.0 4.2.1 4.3.0 4.3.1 4.3.2 4.4.0 4.5.0 4.6.0 4.7.0 4.7.1 4.7.2 4.7.3 4.8.0
wp-staging / Staging / Service / Database / TableCreateService.php
wp-staging / Staging / Service / Database Last commit date
RowsExporter.php 1 week ago TableCreateService.php 1 week ago
TableCreateService.php
193 lines
1 <?php
2
3 namespace WPStaging\Staging\Service\Database;
4
5 use RuntimeException;
6 use WPStaging\Backup\Service\Database\DatabaseImporter;
7 use WPStaging\Framework\Adapter\Database;
8 use WPStaging\Framework\Database\TableService;
9 use WPStaging\Vendor\Psr\Log\LoggerInterface;
10
11 /**
12 * This class is similar to the src\Framework\CloningProcess\Database\DatabaseCloningService class.
13 * It is adjusted to use Backuper Logic and it only focus on creating a table in the staging site.
14 * @see src\Framework\CloningProcess\Database\DatabaseCloningService for existing logic
15 * @see src\Staging\Service\Database\RowsCopier for logic related to copying tables rows
16 */
17 class TableCreateService
18 {
19 /** @var LoggerInterface */
20 protected $logger;
21
22 /** @var Database */
23 protected $sourceDb;
24
25 /** @var Database */
26 protected $destinationDb;
27
28 /** @var TableService */
29 protected $tableService;
30
31 /** @var string */
32 protected $databaseName;
33
34 /** @var string */
35 protected $sourcePrefix;
36
37 /** @var string */
38 protected $destinationPrefix;
39
40 /** @var bool */
41 protected $isResetExistingTables = false;
42
43 /**
44 * @param Database $database
45 */
46 public function __construct(Database $sourceDb, TableService $tableService)
47 {
48 $this->sourceDb = $sourceDb;
49 $this->tableService = $tableService;
50 }
51
52 public function setup(LoggerInterface $logger, Database $destinationDb)
53 {
54 $this->logger = $logger;
55 $this->destinationDb = $destinationDb;
56 $this->databaseName = $this->sourceDb->getWpdba()->getClient()->__get('dbname');
57 $this->sourcePrefix = $this->sourceDb->getPrefix();
58 $this->destinationPrefix = $this->destinationDb->getPrefix();
59 }
60
61 public function getTableWithoutPrefix(string $srcTableName): string
62 {
63 if (strpos($srcTableName, $this->sourcePrefix) !== 0) {
64 return $srcTableName;
65 }
66
67 return substr($srcTableName, strlen($this->sourcePrefix));
68 }
69
70 public function getDestinationTable(string $srcTableName): string
71 {
72 if (empty($srcTableName)) {
73 throw new RuntimeException("Get Destination Table - Source table name is empty");
74 }
75
76 if (empty($this->destinationPrefix)) {
77 throw new RuntimeException("Get Destination Table - Destination table prefix is empty");
78 }
79
80 if (strpos($srcTableName, $this->sourcePrefix) === 0) {
81 return $this->destinationPrefix . substr($srcTableName, strlen($this->sourcePrefix));
82 }
83
84 $basePrefix = $this->sourceDb->getBasePrefix();
85 if (
86 in_array($srcTableName, [
87 $basePrefix . 'users',
88 $basePrefix . 'usermeta',
89 ], true)
90 ) {
91 return $this->destinationPrefix . substr($srcTableName, strlen($basePrefix));
92 }
93
94 return $this->destinationPrefix . $srcTableName;
95 }
96
97 /**
98 * @param bool $isResetExistingTables
99 * @return void
100 */
101 public function setIsResetExistingTables(bool $isResetExistingTables)
102 {
103 $this->isResetExistingTables = $isResetExistingTables;
104 }
105
106 public function isTableExist(string $tableName): bool
107 {
108 return $this->tableService->tableExists($tableName);
109 }
110
111 public function preserveExistingTable(string $tableName, string $tableWithoutPrefix): string
112 {
113 $newTableName = DatabaseImporter::TMP_DATABASE_PREFIX_TO_DROP . $tableWithoutPrefix;
114 $this->dropOrphanedBackupTable($newTableName);
115 $this->logger->info(sprintf("Preserving existing table %s by renaming it to %s", esc_html($tableName), esc_html($newTableName)));
116 if ($this->tableService->renameTable($tableName, $newTableName)) {
117 return $newTableName;
118 }
119
120 throw new RuntimeException("Cleanup Table - Cannot preserve existing table. Error: Unable to rename table $tableName to $newTableName");
121 }
122
123 /**
124 * An interrupted earlier job can leave a backup table behind, which would make the
125 * preserve rename collide and abort the whole clone/update. These
126 * TMP_DATABASE_PREFIX_TO_DROP tables are transient, so a stale one is always safe to
127 * drop before re-preserving.
128 *
129 * @param string $backupTableName
130 * @return void
131 */
132 protected function dropOrphanedBackupTable(string $backupTableName)
133 {
134 if (!$this->tableService->tableExists($backupTableName)) {
135 return;
136 }
137
138 $this->logger->warning(sprintf("Cleanup Table - Dropping orphaned backup table %s left by a previous job", esc_html($backupTableName)));
139 $this->tableService->dropTable($backupTableName);
140 }
141
142 /**
143 * @param string $destTableName
144 * @param string $srcTableName
145 * @return void
146 */
147 public function createDestinationTable(string $srcTableName, string $destTableName)
148 {
149 $this->logger->info(sprintf("Creating table %s", esc_html($destTableName)));
150 $this->dropDestinationTableIfExists($destTableName);
151
152 $createTableQuery = $this->tableService->getCreateTableQuery($srcTableName);
153 if (empty($createTableQuery)) {
154 throw new RuntimeException("Create Table - Cannot clone table $srcTableName to $destTableName. Error: Unable to find create table query");
155 }
156
157 $createTableQuery = str_replace($srcTableName, $destTableName, $createTableQuery);
158 $createTableQuery = $this->tableService->replaceTableConstraints($createTableQuery);
159 $createTableQuery = $this->tableService->replaceTableOptions($createTableQuery);
160 if (empty($createTableQuery)) {
161 throw new RuntimeException("Create Table - Cannot clone table $srcTableName to $destTableName. Error: Unable to replace contraints");
162 }
163
164 $result = $this->destinationDb->getClient()->query($createTableQuery);
165 if ($result === false) {
166 $error = $this->destinationDb->getWpdb()->last_error;
167 throw new RuntimeException("Create Table - Cannot clone table $srcTableName to $destTableName. Error: $error");
168 }
169 }
170
171 /**
172 * @param string $destTableName
173 * @return void
174 */
175 protected function dropDestinationTableIfExists(string $destTableName)
176 {
177 if (!$this->tableService->tableExists($destTableName)) {
178 return;
179 }
180
181 if (!$this->isResetExistingTables) {
182 throw new RuntimeException("Create Table - Cannot clone table. Error: Destination table $destTableName already exists.");
183 }
184
185 $this->logger->warning(sprintf("Create Table - Table %s already exists, dropping it first", esc_html($destTableName)));
186 if ($this->tableService->dropTable($destTableName)) {
187 return;
188 }
189
190 throw new RuntimeException("Create Table - Cannot drop table $destTableName");
191 }
192 }
193