PluginProbe ʕ •ᴥ•ʔ
WP STAGING – WordPress Backups, Restore, Migration & Clone / 4.11.0
WP STAGING – WordPress Backups, Restore, Migration & Clone v4.11.0
4.11.0 4.10.0 4.9.5 4.9.4 4.9.3 4.9.2 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 / Framework / Database / SearchReplace.php
wp-staging / Framework / Database Last commit date
Exporter 1 day ago QueryBuilder 1 day ago CustomTable.php 1 day ago DbInfo.php 1 day ago ExcludedTables.php 1 day ago ExternalDatabaseConfiguration.php 1 day ago OptionPreservationHandler.php 1 day ago SearchReplace.php 1 day ago SelectedTables.php 1 day ago TableDto.php 1 day ago TableService.php 1 day ago TablesRenamer.php 1 day ago WpDbInfo.php 1 day ago WpOptionsInfo.php 1 day ago iDbInfo.php 2 years ago
SearchReplace.php
314 lines
1 <?php
2
3 namespace WPStaging\Framework\Database;
4
5 use WPStaging\Framework\Facades\Hooks;
6 use WPStaging\Framework\Traits\DebugLogTrait;
7 use WPStaging\Framework\Traits\SerializeTrait;
8 use WPStaging\Framework\Traits\UrlTrait;
9
10
11
12
13
14 class SearchReplace
15 {
16 use DebugLogTrait;
17 use SerializeTrait;
18 use UrlTrait;
19
20
21
22
23
24
25
26
27
28 const FILTER_REPLACE_EXTENDED_DATA = 'wpstg.database.searchreplace.replace_extended_data';
29
30
31 private $search;
32
33
34 private $replace;
35
36
37 private $exclude;
38
39
40 private $caseSensitive;
41
42
43 private $currentSearch;
44
45
46 private $currentReplace;
47
48
49 private $isWpBakeryActive;
50
51 protected $smallerReplacement = PHP_INT_MAX;
52
53 public function __construct(array $search = [], array $replace = [], $caseSensitive = true, array $exclude = [])
54 {
55 $this->search = $search;
56 $this->replace = $replace;
57 $this->caseSensitive = $caseSensitive;
58 $this->exclude = $exclude;
59 $this->isWpBakeryActive = false;
60 }
61
62
63
64
65 public function getSmallerSearchLength()
66 {
67 if ($this->smallerReplacement < PHP_INT_MAX) {
68 return $this->smallerReplacement;
69 }
70
71 foreach ($this->search as $search) {
72 if (strlen($search) < $this->smallerReplacement) {
73 $this->smallerReplacement = strlen($search);
74 }
75 }
76
77 return $this->smallerReplacement;
78 }
79
80
81
82
83
84 public function replace($data)
85 {
86 if (defined('DISABLE_WPSTG_SEARCH_REPLACE') && (bool)DISABLE_WPSTG_SEARCH_REPLACE) {
87 return $data;
88 }
89
90 if (!$this->search || !$this->replace) {
91 return $data;
92 }
93
94 $totalSearch = count($this->search);
95 $totalReplace = count($this->replace);
96 if ($totalSearch !== $totalReplace) {
97 throw new \RuntimeException(
98 sprintf(
99 'Can not search and replace. There are %d items to search and %d items to replace',
100 $totalSearch,
101 $totalReplace
102 )
103 );
104 }
105
106 for ($i = 0; $i < $totalSearch; $i++) {
107 $this->currentSearch = (string)$this->search[$i];
108 $this->currentReplace = (string)$this->replace[$i];
109 $data = $this->walker($data);
110 }
111
112 return $data;
113 }
114
115
116
117
118
119
120
121
122 public function replaceExtended($data)
123 {
124 if (defined('DISABLE_WPSTG_SEARCH_REPLACE') && (bool)DISABLE_WPSTG_SEARCH_REPLACE) {
125 return $data;
126 }
127
128 if ($this->isWpBakeryActive) {
129 $data = preg_replace_callback('/\[vc_raw_html\](.+?)\[\/vc_raw_html\]/S', [$this, 'replaceWpBakeryValues'], $data);
130 }
131
132 $data = $this->replace($data);
133
134 if (!function_exists('has_filter') || has_filter(self::FILTER_REPLACE_EXTENDED_DATA) === false) {
135 return $data;
136 }
137
138 return Hooks::applyFilters(self::FILTER_REPLACE_EXTENDED_DATA, $data, $this->search, $this->replace);
139 }
140
141 public function replaceWpBakeryValues($matched)
142 {
143 $data = $this->base64Decode($matched[1]);
144 $data = $this->replace($data);
145 return '[vc_raw_html]' . base64_encode($data) . '[/vc_raw_html]';
146 }
147
148 public function setSearch(array $search)
149 {
150 $this->search = $search;
151 return $this;
152 }
153
154 public function setReplace(array $replace)
155 {
156 $this->replace = $replace;
157 return $this;
158 }
159
160
161
162
163
164
165
166
167
168
169
170 public function appendSearchReplacePair(string $search, string $replace)
171 {
172 $this->search[] = $search;
173 $this->replace[] = $replace;
174 $this->smallerReplacement = PHP_INT_MAX;
175 return $this;
176 }
177
178 public function setCaseSensitive($caseSensitive)
179 {
180 $this->caseSensitive = $caseSensitive;
181 return $this;
182 }
183
184 public function setExclude(array $exclude)
185 {
186 $this->exclude = $exclude;
187 return $this;
188 }
189
190
191
192
193
194
195 public function setWpBakeryActive($isActive = true)
196 {
197 $this->isWpBakeryActive = $isActive;
198 return $this;
199 }
200
201
202
203
204
205 private function walker($data)
206 {
207 switch (gettype($data)) {
208 case "string":
209 return $this->replaceString($data);
210 case "array":
211 return $this->replaceArray($data);
212 case "object":
213 return $this->replaceObject($data);
214 }
215
216 return $data;
217 }
218
219
220
221
222
223 private function replaceString($data)
224 {
225 if (!$this->isSerialized($data)) {
226 return $this->strReplace($data);
227 }
228
229
230 if (strpos($data, 'O:3:"PDO":0:') !== false) {
231 return $data;
232 }
233
234
235
236
237 if (strpos($data, 'O:8:"DateTime":0:') !== false) {
238 return $data;
239 }
240
241
242
243
244 if (strpos($data, 'O:') !== false && preg_match_all('@O:\d+:"([^"]+)"@', $data, $match) && !empty($match) && !empty($match[1])) {
245 foreach ($match[1] as $value) {
246 if ($value !== 'stdClass') {
247 return $data;
248 }
249 }
250
251 unset($match);
252 }
253
254 $unserialized = false;
255 try {
256 $unserialized = @unserialize($data);
257 } catch (\Throwable $e) {
258 $this->debugLog('replaceString. Can not unserialize data. Error: ' . $e->getMessage() . ' Data: ' . $data);
259 }
260
261 if ($unserialized !== false) {
262 return serialize($this->walker($unserialized));
263 }
264
265 return $data;
266 }
267
268 private function replaceArray(array $data)
269 {
270 foreach ($data as $key => $value) {
271 $data[$key] = $this->walker($value);
272 }
273
274 return $data;
275 }
276
277 private function replaceObject($data)
278 {
279
280
281
282 $props = get_object_vars($data);
283 if (!empty($props['__PHP_Incomplete_Class_Name'])) {
284 return $data;
285 }
286
287 foreach ($props as $key => $value) {
288 if ($key === '' || (isset($key[0]) && ord($key[0]) === 0)) {
289 continue;
290 }
291
292 $data->{$key} = $this->walker($value);
293 }
294
295 return $data;
296 }
297
298 private function strReplace($data = '')
299 {
300 $regexExclude = '';
301 foreach ($this->exclude as $excludeString) {
302
303 $regexExclude .= $excludeString . '(*SKIP)(*FAIL)|';
304 }
305
306 $pattern = '#' . $regexExclude . preg_quote($this->currentSearch, '#') . '#';
307 if (!$this->caseSensitive) {
308 $pattern .= 'i';
309 }
310
311 return preg_replace($pattern, $this->currentReplace, $data);
312 }
313 }
314