PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / helpers / libraries / backup / export / rule / sql.php
vikappointments / site / helpers / libraries / backup / export / rule Last commit date
folder.php 6 days ago sql.php 6 days ago sqlfile.php 6 days ago sqlplain.php 6 days ago
sql.php
287 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 VAPLoader::import('libraries.backup.export.rule');
15
16 /**
17 * SQL Backup export rule.
18 *
19 * @since 1.7.1
20 */
21 class VAPBackupExportRuleSql extends VAPBackupExportRule
22 {
23 /**
24 * An array of SQL statements.
25 *
26 * @var array
27 */
28 protected $queries = [];
29
30 /**
31 * The database table.
32 *
33 * @var string
34 */
35 protected $table;
36
37 /**
38 * The information of the database columns.
39 *
40 * @var object
41 */
42 private $columns;
43
44 /**
45 * Indicates the maximum number of rows under the same INSERT.
46 *
47 * @var integer
48 */
49 private $maxRowsPerInsert = 100;
50
51 /**
52 * Returns the rules instructions.
53 *
54 * @return mixed
55 */
56 public function getData()
57 {
58 return $this->queries;
59 }
60
61 /**
62 * Configures the rule to work according to the specified data.
63 *
64 * @param string $data The database table name.
65 *
66 * @return void
67 */
68 protected function setup($data)
69 {
70 $dbo = JFactory::getDbo();
71
72 // register table
73 $this->table = $data;
74
75 // get all the columns of the table to export
76 $this->columns = $dbo->getTableColumns($this->table, $typeOnly = false);
77
78 // get total count of records
79 $count = $this->getCount();
80
81 if (!$count)
82 {
83 // nothing to export
84 return;
85 }
86
87 // get current database prefix
88 $prefix = $dbo->getPrefix();
89
90 // shows the table CREATE statement that creates the given table
91 $createLookup = $dbo->getTableCreate($this->table);
92
93 // check whether the current drivers supports a tool to return the statement that
94 // was used to create the database table
95 if (isset($createLookup[$this->table]))
96 {
97 // extract statement from create lookup and replace prefix
98 $create = preg_replace("/`{$prefix}(vikappointments_(?:[a-z0-9_]+))`/i", '`#__$1`', $createLookup[$this->table]);
99
100 // register query to recreate the table from scratch
101 $this->registerQuery("DROP TABLE IF EXISTS `{$this->table}`");
102 $this->registerQuery($create);
103
104 // we don't need to alter the auto increment because it is already included
105 // within the create table statement
106 $alter_auto_increment = false;
107 }
108 else
109 {
110 // cannot fetch create table statment, truncate the table and assume (or "hope")
111 // that the database structure is the same
112 $this->registerQuery("TRUNCATE TABLE `$this->table`");
113
114 // update auto increment after copying all the records
115 $alter_auto_increment = true;
116 }
117
118 $insertQuery = $dbo->getQuery(true);
119
120 // prepare INSERT query
121 $insertQuery->insert($dbo->qn($this->table));
122
123 $dispatcher = VAPFactory::getEventDispatcher();
124
125 /**
126 * Trigger event to allow third party plugins to choose what are the columns to dump
127 * and whether the table should be skipped or not.
128 *
129 * Fires while attaching a rule to dump some SQL statements.
130 *
131 * @param array &$columns An associative array of supported database table columns,
132 * where the key is the column name and the value is a nested
133 * array holding the column information.
134 * @param string $table The name of the database table.
135 *
136 * @return boolean False to avoid including the table into the backup.
137 *
138 * @since 1.7.1
139 */
140 if ($dispatcher->false('onBeforeBackupDumpSqlVikAppointments', [&$this->columns, $this->table]))
141 {
142 // a third-party plugin decided to skip the table
143 return;
144 }
145
146 // iterate the columns
147 foreach ($this->columns as $column => $type)
148 {
149 $insertQuery->columns($dbo->qn($column));
150 }
151
152 // create SELECT query
153 $selectQuery = $dbo->getQuery(true)->select('*')->from($dbo->qn($this->table));
154
155 $offset = 0;
156
157 while ($offset < $count)
158 {
159 $dbo->setQuery($selectQuery, $offset, $this->maxRowsPerInsert);
160 $rows = $dbo->loadObjectList();
161
162 // clear previous values
163 $insertQuery->clear('values');
164
165 foreach ($rows as $row)
166 {
167 $values = array();
168
169 foreach ($this->columns as $k => $type)
170 {
171 if (!isset($row->{$k}))
172 {
173 // use NULL operator
174 $values[] = 'NULL';
175 }
176 else
177 {
178 // escape the value
179 $values[] = $dbo->q($row->{$k});
180 }
181 }
182
183 $insertQuery->values(implode(',', $values));
184 }
185
186 // register query
187 $this->registerQuery((string) $insertQuery);
188
189 // increase offset
190 $offset += $this->maxRowsPerInsert;
191
192 // free space
193 unset($rows);
194 }
195
196 // fetch table auto increment
197 if ($alter_auto_increment && ($ai = $this->getAutoIncrement($this->table)))
198 {
199 $this->registerQuery("ALTER TABLE `{$this->table}` AUTO_INCREMENT = {$ai}");
200 }
201 }
202
203 /**
204 * Helper method used to register the query inside the buffer.
205 *
206 * @param string $query The query to register.
207 *
208 * @return void
209 */
210 protected function registerQuery($query)
211 {
212 /**
213 * @wponly
214 *
215 * While escaping a SQL string, WordPress replaces any "%" character with a random hash.
216 * Normally that hash is automatically unescaped while executing the query, but in our case,
217 * since we are dumping the query for later use, we have to manually unescape it, otherwise
218 * the WordPress website that is going to import the dump won't be able to revert the hash
219 * back, because the latter changes at every page loading.
220 *
221 * @since 1.7.4
222 */
223 if (VersionListener::isWordpress())
224 {
225 $query = JFactory::getDbo()->remove_placeholder_escape($query);
226 }
227
228 if (!preg_match("/;$/", $query))
229 {
230 $query .= ';';
231 }
232
233 $this->queries[] = $query;
234 }
235
236 /**
237 * Counts the total number of rows inside the table.
238 *
239 * @return integer
240 */
241 private function getCount()
242 {
243 $dbo = JFactory::getDbo();
244
245 // count rows
246 $q = $dbo->getQuery(true)->select('COUNT(1)')->from($dbo->qn($this->table));
247
248 $dbo->setQuery($q);
249 return (int) $dbo->loadResult();
250 }
251
252 /**
253 * Fetches the correct auto increment to set for the given table.
254 *
255 * @return mixed The auto increment on success, null otherwise.
256 */
257 private function getAutoIncrement()
258 {
259 $dbo = JFactory::getDbo();
260
261 $pk = null;
262
263 // look for the primary key
264 foreach ($this->columns as $column => $info)
265 {
266 if ($info->Extra === 'auto_increment')
267 {
268 $pk = $column;
269 }
270 }
271
272 if (!$pk)
273 {
274 // no primary keys with auto increment
275 return null;
276 }
277
278 // fetch highest ID
279 $q = $dbo->getQuery(true)->select('MAX(' . $dbo->qn($pk) . ')')->from($dbo->qn($this->table));
280
281 $dbo->setQuery($q);
282 $dbo->execute();
283
284 return (int) $dbo->loadResult() + 1;
285 }
286 }
287