PluginProbe
Import WP – CSV & XML Import Export for WordPress / 2.15.0
Import WP – CSV & XML Import Export for WordPress v2.15.0
2.15.1 2.15.0 2.14.24 2.14.23 2.7.0 2.7.1 2.7.10 2.7.11 2.7.12 2.7.13 2.7.14 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7 2.7.8 2.7.9 2.8.0 2.8.1 2.8.2 2.8.3 2.9.0 2.9.1 All 144 releases
jc-importer / class / Common / Importer / Parser / AbstractParser.php

AbstractParser.php in Import WP – CSV & XML Import Export for WordPress 2.15.0, at class/Common/Importer/Parser/AbstractParser.php

388 lines 11.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace ImportWP\Common\Importer\Parser;
4
5 use ImportWP\Common\Importer\FileInterface;
6
7 abstract class AbstractParser
8 {
9 /**
10 * @var \ImportWP\Common\Importer\FileInterface $file
11 */
12 protected $file;
13
14 protected $record_index;
15 protected $record;
16
17 /**
18 * Parser constructor.
19 *
20 * @param \ImportWP\Common\Importer\FileInterface $file
21 */
22 public function __construct(FileInterface $file)
23 {
24 $this->file = $file;
25 }
26
27 public function getRecord($record_index = 0)
28 {
29
30 if ($this->record_index !== $record_index) {
31 $this->record_index = $record_index;
32 $this->record = $this->file->getRecord($this->getRecordIndex());
33 $this->onRecordLoaded();
34 }
35
36 return $this;
37 }
38
39 public function getRecordIndex()
40 {
41 return $this->record_index;
42 }
43
44 abstract protected function onRecordLoaded();
45
46 public function queryGroup($group)
47 {
48 $output = [];
49
50 if (isset($group['fields'])) {
51
52 // find files matching ._mapped._index
53
54 $maps = [];
55 foreach ($group['fields'] as $field_key => $field_value) {
56
57 $matches = [];
58 if (
59 // post.post_name._mapped._index
60 // custom_fields.0.value._mapped._index
61 preg_match('/^(.*?)\._mapped\._index$/', $field_key, $matches) === 1 &&
62 // ignore custom_fields.0._mapped._index old style FieldMapper
63 preg_match('/^custom_fields\.(?:[0-9]+)\._mapped\._index$/', $field_key) === 0 &&
64 intval($field_value) > 0
65 ) {
66 $maps[$matches[1]] = $field_value;
67 } else {
68 $output[$field_key] = $this->query_string($field_value);
69 }
70 }
71 }
72
73 if (!empty($maps)) {
74 foreach ($maps as $field_key => $field_rows) {
75
76 $data = [];
77 $delimiter = false;
78
79 if (isset($output[$field_key . '._mapped._delimiter']) && !empty($output[$field_key . '._mapped._delimiter'])) {
80
81 // get delimiter from FieldMap delimiter field, this has priority
82 $delimiter = $output[$field_key . '._mapped._delimiter'];
83 } else {
84
85 // get delimiter from parent section settings. e.g. taxonomies and attachments
86 $lastPos = strrpos($field_key, '.');
87 if ($lastPos !== false) {
88 $tmp = substr($field_key, 0, $lastPos);
89 if (isset($output[$tmp . '.settings._delimiter'])) {
90 $delimiter = !empty($output[$tmp . '.settings._delimiter']) ? $output[$tmp . '.settings._delimiter'] : ',';
91 }
92 }
93 }
94
95 foreach ($output as $item_key => $item_value) {
96 $matches = [];
97 if (preg_match('/^' . $field_key . '\._mapped\.([0-9]+)\.(.*?)$/', $item_key, $matches) === 1) {
98
99 // row is to high
100 if (intval($matches[1]) >= intval($field_rows)) {
101
102 unset($output[$matches[0]]);
103 continue;
104 }
105
106 if (!isset($data[$matches[1]])) {
107 $data[$matches[1]] = [];
108 }
109
110 $data[$matches[1]][$matches[2]] = $item_value;
111
112 unset($output[$matches[0]]);
113 }
114 }
115
116 if (!empty($delimiter)) {
117
118 $field_parts = explode($delimiter, $output[$field_key]);
119 foreach ($field_parts as $k => $field_part) {
120 $field_parts[$k] = $this->map_field_data($field_part, array_values($data));
121 }
122 $output[$field_key] = implode($delimiter, $field_parts);
123 } else {
124 $output[$field_key] = $this->map_field_data($output[$field_key], array_values($data));
125 }
126 }
127 }
128
129 return $output;
130 }
131
132 /**
133 * Parse Query String for {} run query on them
134 *
135 * @param string $query
136 *
137 * @return string
138 */
139 public function query_string($query)
140 {
141 // Parse [iwp:method(...)] before substituting {column} values, otherwise
142 // parentheses in the data (e.g. Excel =Hyperlink("url")) can close a
143 // malformed mapping that is missing ")".
144 return $this->interpolate_braces($this->handle_custom_methods($query));
145 }
146
147 public function query_matches($matches)
148 {
149 if (isset($matches[0]) && isset($matches[1])) {
150 return $this->query($matches[1]);
151 }
152
153 return '';
154 }
155
156 abstract public function query($query);
157
158 public function file()
159 {
160 return $this->file;
161 }
162
163 public function handle_custom_methods($input)
164 {
165 // Prefixed [iwp:method(...)] to avoid colliding with shortcodes / Gutenberg content.
166 if (!is_string($input) || $input === '' || strpos($input, '[iwp:') === false) {
167 return $input;
168 }
169
170 $offset = 0;
171 $output = '';
172 $length = strlen($input);
173
174 while (($start = strpos($input, '[iwp:', $offset)) !== false) {
175 $output .= substr($input, $offset, $start - $offset);
176
177 $parsed = $this->parse_custom_method_at($input, $start, $length);
178 if ($parsed === null) {
179 $output .= '[iwp:';
180 $offset = $start + 5;
181 continue;
182 }
183
184 $output .= $parsed[1];
185 $offset = $parsed[0];
186 }
187
188 return $output . substr($input, $offset);
189 }
190
191 /**
192 * Substitute {column} / {xpath} selectors.
193 *
194 * @param string $query
195 * @return string
196 */
197 private function interpolate_braces($query)
198 {
199 if (!is_string($query) || $query === '' || strpos($query, '{') === false) {
200 return $query;
201 }
202
203 return preg_replace_callback('/{(.*?)}/', array($this, 'query_matches'), $query);
204 }
205
206 /**
207 * @param string $input
208 * @param int $start
209 * @param int $length
210 * @return array{0:int,1:string}|null End offset and replacement, or null if not a complete call.
211 */
212 private function parse_custom_method_at($input, $start, $length)
213 {
214 $i = $start + 5;
215 $name_len = strspn($input, 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_', $i);
216 if ($name_len < 1) {
217 return null;
218 }
219
220 $method = substr($input, $i, $name_len);
221 $i += $name_len;
222 if ($i >= $length || $input[$i] !== '(') {
223 return null;
224 }
225 $i++;
226
227 $extracted = $this->extract_balanced_method_args($input, $i, $length);
228 if ($extracted === null) {
229 return null;
230 }
231
232 list($raw_args, $after_paren) = $extracted;
233 if ($after_paren >= $length || $input[$after_paren] !== ']') {
234 return null;
235 }
236
237 $end = $after_paren + 1;
238 $original = substr($input, $start, $end - $start);
239 $args = $this->split_custom_method_args($raw_args);
240 foreach ($args as &$arg) {
241 $arg = $this->interpolate_braces($arg);
242 }
243 unset($arg);
244
245 if (is_callable($method)) {
246 return array($end, call_user_func_array($method, $args));
247 }
248
249 return array($end, $original);
250 }
251
252 /**
253 * Read until the method's closing ")", ignoring parentheses inside quotes.
254 *
255 * @param string $input
256 * @param int $start
257 * @param int $length
258 * @return array{0:string,1:int}|null Raw args and offset after ")", or null if unterminated.
259 */
260 private function extract_balanced_method_args($input, $start, $length)
261 {
262 $depth = 1;
263 $quote = null;
264 for ($i = $start; $i < $length; $i++) {
265 $ch = $input[$i];
266 if ($quote !== null) {
267 if ($ch === $quote) {
268 $quote = null;
269 }
270 continue;
271 }
272 if ($ch === '"' || $ch === "'") {
273 $quote = $ch;
274 continue;
275 }
276 if ($ch === '(') {
277 $depth++;
278 continue;
279 }
280 if ($ch === ')') {
281 $depth--;
282 if ($depth === 0) {
283 return array(substr($input, $start, $i - $start), $i + 1);
284 }
285 }
286 }
287
288 return null;
289 }
290
291 /**
292 * @param string $raw_args
293 * @return array
294 */
295 private function split_custom_method_args($raw_args)
296 {
297 $args = [];
298 if ($raw_args === '') {
299 return $args;
300 }
301
302 // Dont split comma's if they are inside a double quote
303 if (preg_match_all('/(?:".*?"|[^",\s]+)(?=\s*,|\s*$)/s', $raw_args, $result) > 0) {
304 $args = $result[0];
305 foreach ($args as &$arg) {
306 // Strip quotes from start and end of string
307 $arg = preg_replace('/^(\'(.*)\'|"(.*)")$/s', '$2$3', $arg);
308 }
309 unset($arg);
310 }
311
312 return $args;
313 }
314
315 public function map_field_data($input, $map)
316 {
317 foreach ($map as $map_data) {
318
319 $condition = isset($map_data['_condition']) && !empty($map_data['_condition']) ? $map_data['_condition'] : 'equal';
320 $key = isset($map_data['key']) ? $map_data['key'] : '';
321 $output = isset($map_data['value']) ? $map_data['value'] : '';
322
323 switch ($condition) {
324
325 case 'gt':
326 $left = intval($input);
327 $right = intval($key);
328 if ($left > $right) {
329 return $output;
330 }
331 break;
332 case 'gte':
333 $left = intval($input);
334 $right = intval($key);
335 if ($left >= $right) {
336 return $output;
337 }
338 break;
339 case 'lt':
340 $left = intval($input);
341 $right = intval($key);
342 if ($left < $right) {
343 return $output;
344 }
345 break;
346 case 'lte':
347 $left = intval($input);
348 $right = intval($key);
349 if ($left <= $right) {
350 return $output;
351 }
352 break;
353 case 'contains':
354 if (stripos($input, trim($key)) !== false) {
355 return $output;
356 }
357 break;
358 case 'in':
359 if (in_array($input, explode(',', $key))) {
360 return $output;
361 }
362 break;
363 case 'not-equal':
364 if (trim($key) !== trim($input)) {
365 return $output;
366 }
367 break;
368 case 'not-contains':
369 if (stripos($input, trim($key)) === false) {
370 return $output;
371 }
372 break;
373 case 'not-in':
374 if (!in_array($input, explode(',', $key))) {
375 return $output;
376 }
377 break;
378 default:
379 if (trim($key) === trim($input)) {
380 return $output;
381 }
382 break;
383 }
384 }
385 return $input;
386 }
387 }
388