Bitrix-D7 23.9
 
Загрузка...
Поиск...
Не найдено
stringtemplateconverter.php
1<?php
2
4
8
10{
11 private const STR_DELIMITER_PLACEHOLDER = '#S#';
12 private const REGEX_COMMA_AMONG_EMPTY_SPACE = '\\s*,\\s*';
13 private const REGEX_GROUP_DELIMITER = '(\\"([^"\\\\]*|\\\\"|\\\\\\\\|\\\\)*")';
14 private const REGEX_GROUP_FIELD_TEXT = self::REGEX_GROUP_DELIMITER;
15 private const REGEX_GROUP_FIELD_NAME = '([a-zA-Z][a-zA-Z_0-9]*(:(NU|UN|N|U))?)';
16 private const REGEX_GROUP_FIELD_LIST_END = '\\s*\\]';
17 private const REGEX_GROUP_END = self::REGEX_GROUP_FIELD_LIST_END;
18 private const REGEX_PART_FROM_DELIMITER_TO_FIELD_LIST = '\\s*,\\s*\\[\\s*';
19 private const REGEX_GROUP_PART_BEFORE_FIELDS =
20 '(([^\\[\\\\]|\\\\\\[|\\\\\\\\)*)(\\[\\s*)("([^"\\\\]*|\\\\"|\\\\\\\\|\\\\)*")\\s*,\\s*\\[\\s*';
21
22 private const ERR_PARSE_GROUP_START_POSITION = 1100;
23 private const ERR_PARSE_GROUP_START = 1110;
24 private const ERR_PARSE_GROUP_DELIMITER = 1120;
25 private const ERR_PARSE_PART_FROM_DELIMITER_TO_FIELD_LIST = 1130;
26 private const ERR_PARSE_GROUP_FIELD_TEXT = 1140;
27 private const ERR_PARSE_GROUP_FIELD_NAME = 1150;
28 private const ERR_PARSE_GROUP_FIELD = 1160;
29 private const ERR_PARSE_GROUP_FIELD_LIST = 1170;
30 private const ERR_PARSE_GROUP_FIELD_LIST_DELIMITER = 1180;
31 private const ERR_PARSE_GROUP_FIELD_LIST_END = 1190;
32 private const ERR_PARSE_GROUP_END = 1200;
33 private const ERR_PARSE_GROUP = 1210;
34
36 private $template = '';
37
39 private $delimiter;
40
42 private $htmlEncode;
43
45 private $format;
46
47 public function __construct(string $template, string $delimiter, bool $htmlEncode, Format $format = null)
48 {
49 $this->template = $template;
50 $this->delimiter = $delimiter;
51 $this->htmlEncode = $htmlEncode;
52 $this->format = $format;
53 }
54
55 private function getErrorCodes(): array
56 {
57 static $errorMap = null;
58
59 if ($errorMap !== null)
60 {
61 return $errorMap;
62 }
63
64 $errorMap = [];
65 $refClass = new \ReflectionClass(__CLASS__);
66 foreach ($refClass->getConstants() as $name => $value)
67 {
68 if (substr($name, 0, 4) === 'ERR_')
69 {
70 $errorMap[constant("self::{$name}")] = $name;
71 }
72 }
73
74 return $errorMap;
75 }
76
77 private function getErrorsText(array $context): string
78 {
79 $result = '';
80
81 $errCodes = $this->getErrorCodes();
82 foreach ($context['error']['errors'] as $errInfo)
83 {
84 $result .= "Error: {$errInfo['position']}, {$errCodes[$errInfo['code']]}" . PHP_EOL;
85 if (!empty($errInfo['info']) && is_array($errInfo['info']))
86 {
87 $needHeader = true;
88 foreach($errInfo['info'] as $paramName => $paramValue)
89 {
90 $needPrint = false;
91 if (is_string($paramValue))
92 {
93 $paramValue = "\"{$paramValue}\"";
94 $needPrint = true;
95 }
96 elseif (is_int($paramValue) || is_double($paramValue))
97 {
98 $needPrint = true;
99 }
100 elseif (is_bool($paramValue))
101 {
102 $paramValue = $paramValue ? 'true' : 'false';
103 $needPrint = true;
104 }
105 elseif (is_array($paramValue))
106 {
107 $paramValue = '[...]';
108 $needPrint = true;
109 }
110 elseif (is_object($paramValue))
111 {
112 $paramValue = '{...}';
113 $needPrint = true;
114 }
115 if ($needPrint)
116 {
117 if ($needHeader)
118 {
119 $result .= " Error info:" . PHP_EOL;
120 $needHeader = false;
121 }
122 $result .= " {$paramName}: {$paramValue}" . PHP_EOL;
123 }
124 }
125 }
126 }
127
128 $result .= 'Template: "' . str_replace(["\n", "\""], ['\\n', '\\"'], $context['template']) . '"'
129 . PHP_EOL . PHP_EOL;
130
131 return $result;
132 }
133
134 private function createContext(): array
135 {
136 return [
137 'level' => 0,
138 'position' => 0,
139 'template' => '',
140 'address' => null,
141 'info' => [],
142 'hasError' => false,
143 'error' => [
144 'code' => 0,
145 'position' => 0,
146 'errors' => [],
147 'info' => [],
148 ],
149 ];
150 }
151
152 private function clearContextInfo(array $context): array
153 {
154 $context['info'] = [];
155
156 return $context;
157 }
158
159 private function clearContextError(array $context): array
160 {
161 $context['hasError'] = false;
162 $context['error'] = [
163 'code' => 0,
164 'position' => 0,
165 'errors' => [],
166 'info' => [],
167 ];
168
169 return $context;
170 }
171
172 private function clearContextInfoAndError(array $context): array
173 {
174 $context = $this->clearContextInfo($context);
175 $context = $this->clearContextError($context);
176
177 return $context;
178 }
179
180 private function unescapeText(string $text): string
181 {
182 $result = '';
183
184 $length = strlen($text);
185
186 for ($i = 0; $i < $length; $i++)
187 {
188 if ($text[$i] === '\\')
189 {
190 if (($length - $i) > 1)
191 {
192 $result .= $text[++$i];
193 }
194 }
195 else
196 {
197 $result .= $text[$i];
198 }
199 }
200
201 return $result;
202 }
203
204 private function parseGroupDelimiter(array $context): array
205 {
206 // Capturing the group's separator
207 $delimiterStartPosition = $context['position'];
208 // [", ", [ADDRESS_LINE_1:N,ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
209 // Are looking for ^^^^
210 if (preg_match(
211 '/' . self::REGEX_GROUP_DELIMITER . '/ms' . BX_UTF_PCRE_MODIFIER,
212 $context['template'],
213 $matches,
214 PREG_OFFSET_CAPTURE,
215 $delimiterStartPosition
216 )
217 && $matches[0][1] === $delimiterStartPosition
218 )
219 {
220 $context['info'] = [
221 'position' => $delimiterStartPosition,
222 'end' => $delimiterStartPosition + strlen($matches[0][0]),
223 'value' => $this->unescapeText(
224 substr(
225 $context['template'],
226 $delimiterStartPosition + 1,
227 strlen($matches[1][0]) - 2
228 )
229 ),
230 ];
231 $context['position'] = $context['info']['end'];
232 }
233 else
234 {
235 $this->addContextError($context, self::ERR_PARSE_GROUP_DELIMITER, $delimiterStartPosition);
236 }
237
238 return $context;
239 }
240
241 private function parseFieldText(array $context): array
242 {
243 $textBlockStartPosition = $context['position'];
244 $matches = null;
245 // [", ", [ADDRESS_LINE_1:N,ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
246 // Are looking for ^^^^^^
247 if (preg_match(
248 '/' . self::REGEX_GROUP_FIELD_TEXT . '/ms' . BX_UTF_PCRE_MODIFIER,
249 $context['template'],
250 $matches,
251 PREG_OFFSET_CAPTURE,
252 $context['position']
253 )
254 && $matches[0][1] === $textBlockStartPosition
255 )
256 {
257 $context['info'] = [
258 'type' => 'text',
259 'position' => $textBlockStartPosition,
260 'end' => $textBlockStartPosition + strlen($matches[0][0]),
261 'value' => $this->unescapeText(
262 substr(
263 $context['template'],
264 $textBlockStartPosition + 1,
265 strlen($matches[1][0]) - 2
266 )
267 ),
268 ];
269 $context['position'] = $context['info']['end'];
270 }
271 else
272 {
273 $this->addContextError($context, self::ERR_PARSE_GROUP_FIELD_TEXT, $textBlockStartPosition);
274 }
275
276 return $context;
277 }
278
279 private function splitFieldName(string $fieldName): array
280 {
281 $fieldParts = explode(':', $fieldName);
282 $fieldName = $fieldParts[0] ?? '';
283 $fieldModifiers = $fieldParts[1] ?? '';
284 if (!is_string($fieldModifiers))
285 {
286 $fieldModifiers = '';
287 }
288
289 return [$fieldName, $fieldModifiers];
290 }
291
296 private function isTemplateForFieldExists(string $fieldName): bool
297 {
298 return $this->format && $this->format->getTemplate($fieldName) !== null;
299 }
300
306 private function getFieldValueByTemplate(string $fieldName, Address $address): ?string
307 {
308 if(!$this->isTemplateForFieldExists($fieldName))
309 {
310 return null;
311 }
312
313 $template = $this->format->getTemplate($fieldName)->getTemplate();
314 $templateConverter = new StringTemplateConverter(
315 $template,
316 $this->delimiter,
317 $this->htmlEncode,
318 $this->format
319 );
320
321 return $templateConverter->convert($address);
322 }
323
324 private function getAlterFieldValue(Address $address, int $fieldType): string
325 {
326 $localityValue = $address->getFieldValue(FieldType::LOCALITY);
327 $localityValue = is_string($localityValue) ? $localityValue : '';
328 $result = $address->getFieldValue($fieldType);
329 $result = is_string($result) ? $result : '';
330 if ($result !== '' && $localityValue !== '')
331 {
332 $localityValueUpper = mb_strtoupper($localityValue);
333 $localityValueUpperLength = mb_strlen($localityValueUpper);
334 $targetValueUpper = mb_strtoupper($result);
335 $targetValueUpperLength = mb_strlen($targetValueUpper);
336 if ($targetValueUpperLength >= $localityValueUpperLength)
337 {
338 $targetValueSubstr = mb_substr(
339 $targetValueUpper,
340 $targetValueUpperLength - $localityValueUpperLength
341 );
342 if ($localityValueUpper === $targetValueSubstr)
343 {
344 $result = '';
345 }
346 }
347 }
348
349 return $result;
350 }
351
352 private function getAddressFieldValue(Address $address, string $fieldName, string $fieldModifiers): string
353 {
354 $result = '';
355
356 if (FieldType::isTypeExist($fieldName))
357 {
358 $addressFieldType = constant(FieldType::class.'::'.$fieldName);
359
360 if ($fieldName === 'ADM_LEVEL_1' || $fieldName === 'ADM_LEVEL_2')
361 {
362 // Scratch "Province & Region by Locality"
363 $result = $this->getAlterFieldValue($address, $addressFieldType);
364 }
365 else
366 {
367 $result = $address->getFieldValue($addressFieldType);
368 }
369
370 if ($result === null)
371 {
372 $result = $this->getFieldValueByTemplate($fieldName, $address);
373 }
374 }
375 if (!is_string($result))
376 {
377 $result = '';
378 }
379 if ($result !== '')
380 {
381 if (strpos($fieldModifiers, 'N') !== false)
382 {
383 $result = str_replace(["\r\n", "\n", "\r"], '#S#', $result);
384 }
385 if (strpos($fieldModifiers, 'U') !== false)
386 {
387 $result = mb_strtoupper($result);
388 }
389 }
390
391 return $result;
392 }
393
394 private function parseFieldName(array $context): array
395 {
396 $fieldNameStartPosition = $context['position'];
397 $matches = null;
398 // [", ", [ADDRESS_LINE_1:N,ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
399 // Are looking for ^^^^^^^^^^^^^^^^
400 if ($context['address'] instanceof Address
401 && preg_match(
402 '/' . self::REGEX_GROUP_FIELD_NAME . '/ms' . BX_UTF_PCRE_MODIFIER,
403 $context['template'],
404 $matches,
405 PREG_OFFSET_CAPTURE,
406 $context['position']
407 )
408 && $matches[0][1] === $fieldNameStartPosition
409 )
410 {
411 $context['position'] = $fieldNameStartPosition + strlen($matches[0][0]);
412 list($fieldName, $fieldModifiers) = $this->splitFieldName($matches[0][0]);
413 $fieldValue = $this->getAddressFieldValue($context['address'], $fieldName, $fieldModifiers);
414 $context['info'] = [
415 'type' => 'field',
416 'position' => $fieldNameStartPosition,
417 'end' => $context['position'],
418 'modifiers' => $fieldModifiers,
419 'name' => $fieldName,
420 'value' => $fieldValue,
421 ];
422 }
423 else
424 {
425 $this->addContextError($context, self::ERR_PARSE_GROUP_FIELD_NAME, $fieldNameStartPosition);
426 }
427
428 return $context;
429 }
430
431 private function parseFieldListDelimiter(array $context): array
432 {
433 $markerStartPosition = $context['position'];
434 $matches = null;
435 // [", ", [ADDRESS_LINE_1:N , ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
436 // Are looking for ^^^
437 if (preg_match(
438 '/' . self::REGEX_COMMA_AMONG_EMPTY_SPACE . '/ms' . BX_UTF_PCRE_MODIFIER,
439 $context['template'],
440 $matches,
441 PREG_OFFSET_CAPTURE,
442 $context['position']
443 )
444 && $matches[0][1] === $markerStartPosition
445 )
446 {
447 $context['position'] = $markerStartPosition + strlen($matches[0][0]);
448 }
449 else
450 {
451 $this->addContextError($context, self::ERR_PARSE_GROUP_FIELD_LIST_DELIMITER, $markerStartPosition);
452 }
453
454 return $context;
455 }
456
457 private function parseFieldListEnd(array $context): array
458 {
459 $markerStartPosition = $context['position'];
460 $matches = null;
461 // [", ", [ADDRESS_LINE_1:N,ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
462 // Are looking for ^
463 if (preg_match(
464 '/' . self::REGEX_GROUP_FIELD_LIST_END . '/ms' . BX_UTF_PCRE_MODIFIER,
465 $context['template'],
466 $matches,
467 PREG_OFFSET_CAPTURE,
468 $context['position']
469 )
470 && $matches[0][1] === $markerStartPosition
471 )
472 {
473 $context['position'] = $markerStartPosition + strlen($matches[0][0]);
474 }
475 else
476 {
477 $this->addContextError($context, self::ERR_PARSE_GROUP_FIELD_LIST_END, $markerStartPosition);
478 }
479
480 return $context;
481 }
482
483 private function parseField(array $context): array
484 {
485 $fieldInfo = [];
486 $fieldStartPosition = $context['position'];
487 $errors = [];
488
489 // Checking for the presence of a text block
490 $context = $this->parseFieldText($context);
491
492 if ($context['hasError'])
493 {
494 $this->unshiftError($errors, $context['error']['code'], $context['error']['position']);
495 $context = $this->clearContextInfoAndError($context);
496 // Checking for the presence of a field name
497 $context = $this->parseFieldName($context);
498 }
499
500 if ($context['hasError'])
501 {
502 $this->unshiftError($errors, $context['error']['code'], $context['error']['position']);
503 $context = $this->clearContextInfoAndError($context);
504 // Checking for the presence of a nested group
505 $context = $this->parseGroup($context);
506 if ($context['hasError'])
507 {
508 $this->unshiftError($errors, $context['error']['code'], $context['error']['position']);
509 }
510 else if ($context['info']['position'] > $fieldStartPosition)
511 {
512 // Group found beyond the expected position
513 $this->addContextError($context, self::ERR_PARSE_GROUP_START_POSITION, $fieldStartPosition);
514 $this->unshiftError($errors, $context['error']['code'], $context['error']['position']);
515 }
516 }
517
518 if (!$context['hasError'])
519 {
520 $fieldInfo = $context['info'];
521 $fieldInfo['isFieldListEnd'] = false;
522 $context = $this->clearContextInfo($context);
523
524 // Checking for the presence of a field separator
525 $context = $this->parseFieldListDelimiter($context);
526
527 if ($context['hasError'])
528 {
529 $this->unshiftError($errors, $context['error']['code'], $context['error']['position']);
530 $context = $this->clearContextInfoAndError($context);
531 // Checking for the presence of the end sign of the field list
532 $context = $this->parseFieldListEnd($context);
533 if ($context['hasError'])
534 {
535 $this->unshiftError($errors, $context['error']['code'], $context['error']['position']);
536 }
537 else
538 {
539 $fieldInfo['isFieldListEnd'] = true;
540 }
541 }
542 }
543
544 if ($context['hasError'])
545 {
546 $this->unshiftError($errors, self::ERR_PARSE_GROUP_FIELD, $fieldStartPosition);
547 $this->addContextErrors($context, $errors);
548 }
549 else
550 {
551 $context['info'] = $fieldInfo;
552 }
553
554 return $context;
555 }
556
557 private function parseGroupFieldListStart(array $context): array
558 {
559 $fieldListStartPosition = $context['position'];
560 $fieldValues = [];
561 $matches = null;
562 // [", ", [ADDRESS_LINE_1:N,ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
563 // Are looking for ^^^
564 if (preg_match(
565 '/' . self::REGEX_PART_FROM_DELIMITER_TO_FIELD_LIST . '/ms' . BX_UTF_PCRE_MODIFIER,
566 $context['template'],
567 $matches,
568 PREG_OFFSET_CAPTURE,
569 $context['position']
570 )
571 && $matches[0][1] === $fieldListStartPosition
572 )
573 {
574 $context['position'] = $matches[0][1] + strlen($matches[0][0]);
575 $isFieldListEnd = false;
576 while (!($context['hasError'] || $isFieldListEnd))
577 {
578 $context = $this->parseField($context);
579 if (!$context['hasError'])
580 {
581 $isFieldListEnd = (
582 isset($context['info']['isFieldListEnd'])
583 && $context['info']['isFieldListEnd']
584 );
585 if ($context['info']['value'] !== '')
586 {
587 $fieldValues[] = $context['info']['value'];
588 }
589 $context = $this->clearContextInfo($context);
590 }
591 }
592
593 if (!$context['hasError'])
594 {
595 $context['info'] = ['fieldValues' => $fieldValues];
596 }
597 }
598 else
599 {
600 $this->addContextError(
601 $context,
602 self::ERR_PARSE_PART_FROM_DELIMITER_TO_FIELD_LIST,
603 $fieldListStartPosition
604 );
605 }
606
607 if ($context['hasError'])
608 {
609 $this->addContextError($context, self::ERR_PARSE_GROUP_FIELD_LIST, $fieldListStartPosition);
610 }
611
612 return $context;
613 }
614
615 private function parseGroupStart(array $context): array
616 {
617 $matches = null;
618 // [", ", [ADDRESS_LINE_1:N,ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
619 // Are looking for ^^^^^^^^
620 if (preg_match(
621 '/' . self::REGEX_GROUP_PART_BEFORE_FIELDS . '/ms' . BX_UTF_PCRE_MODIFIER,
622 $context['template'],
623 $matches,
624 PREG_OFFSET_CAPTURE,
625 $context['position']
626 )
627 )
628 {
629 $context['info']['groupStartPosition'] = $matches[3][1];
630 $context['info']['groupDelimiterStartPosition'] = $matches[4][1];
631 }
632 else
633 {
634 $this->addContextError($context, self::ERR_PARSE_GROUP_START, $context['position']);
635 }
636
637 return $context;
638 }
639
640 private function parseGroupEnd(array $context): array
641 {
642 $markerStartPosition = $context['position'];
643 $matches = null;
644 // [", ", [ADDRESS_LINE_1:N,ADDRESS_LINE_2,"Text",LOCALITY,ADM_LEVEL_2]]
645 // Are looking for ^
646 if (preg_match(
647 '/' . self::REGEX_GROUP_END . '/ms' . BX_UTF_PCRE_MODIFIER,
648 $context['template'],
649 $matches,
650 PREG_OFFSET_CAPTURE,
651 $context['position']
652 )
653 && $matches[0][1] === $markerStartPosition
654 )
655 {
656 $context['position'] = $markerStartPosition + strlen($matches[0][0]);
657 }
658 else
659 {
660 $this->addContextError($context, self::ERR_PARSE_GROUP_END, $markerStartPosition);
661 }
662
663 return $context;
664 }
665
666 private function parseGroup(array $context): array
667 {
668 $startSearchPosition = $context['position'];
669 $groupStartPosition = 0;
670 $delimiterValue = '';
671 $fieldValues = [];
672
673 $context['level']++;
674
675 // Checking for the presence of a start of a group
676 $context = $this->parseGroupStart($context);
677
678 if (!$context['hasError'])
679 {
680 // Found a sign of the beginning of a group
681 $groupStartPosition = $context['info']['groupStartPosition'];
682 $context['position'] = $context['info']['groupDelimiterStartPosition'];
683 $context = $this->clearContextInfo($context);
684 $context = $this->parseGroupDelimiter($context);
685 }
686
687 if (!$context['hasError'])
688 {
689 // The value of the group separator was got
690 $delimiterValue = $context['info']['value'];
691 $context = $this->clearContextInfo($context);
692 $context = $this->parseGroupFieldListStart($context);
693 }
694
695 if (!$context['hasError'])
696 {
697 // The values of the field list was got
698 $fieldValues = $context['info']['fieldValues'];
699 $context = $this->clearContextInfo($context);
700 $context = $this->parseGroupEnd($context);
701 }
702
703 if (!$context['hasError'])
704 {
705 // The sign of the end of the group is received, the assembly of the group value.
706 $context['info'] = [
707 'type' => 'group',
708 'position' => $groupStartPosition,
709 'end' => $context['position'],
710 'value' => implode(
711 $delimiterValue,
712 array_unique(
713 $fieldValues // Kremlin,Moscow,Moscow,Russia,103132 -> Kremlin,Moscow,Russia,103132
714 )
715 ),
716 ];
717 }
718
719 $context['level']--;
720
721 if ($context['hasError'])
722 {
723 $this->addContextError(
724 $context,
725 self::ERR_PARSE_GROUP,
726 $startSearchPosition,
727 ['groupStartPosition' => $groupStartPosition]
728 );
729 }
730
731 return $context;
732 }
733
734 private function appendTextBlock(array &$blocks, int $position, string $value)
735 {
736 $lastBlock = end($blocks);
737 $lastBlockIndex = key($blocks);
738 if (is_array($lastBlock) && $lastBlock['type'] === 'text')
739 {
740 $blocks[$lastBlockIndex]['value'] .= $value;
741 $blocks[$lastBlockIndex]['length'] += strlen($value);
742 }
743 else
744 {
745 $blocks[] = [
746 'type' => 'text',
747 'position' => $position,
748 'length' => strlen($value),
749 'value' => $value,
750 ];
751 }
752 }
753
754 private function appendGroupBlock(array &$blocks, int $position, string $value)
755 {
756 $blocks[] = [
757 'type' => 'group',
758 'position' => $position,
759 'length' => strlen($value),
760 'value' => $value,
761 ];
762 }
763
764 private function unshiftError(array &$errors, int $code, int $position, array $info = null)
765 {
766 array_unshift(
767 $errors,
768 [
769 'code' => $code,
770 'position' => $position,
771 'info' => (!empty($info) && is_array($info)) ? $info : [],
772 ]
773 );
774 }
775
776 private function addContextError(array &$context, int $code, int $position, array $info = null)
777 {
778 $context['hasError'] = true;
779 $context['error']['code'] = $code;
780 $context['error']['position'] = $position;
781 $context['error']['info'] = (!empty($info) && is_array($info)) ? $info : [];
782 $this->unshiftError($context['error']['errors'], $code, $position, $info);
783 }
784
785 private function addContextErrors(array &$context, array $errors, array $info = null)
786 {
787 $context['hasError'] = true;
788 $context['error']['code'] = $errors[0]['code'];
789 $context['error']['position'] = $errors[0]['position'];
790 $context['error']['info'] = (!empty($info) && is_array($info)) ? $info : [];
791 array_splice($context['error']['errors'], 0, 0, $errors);
792 }
793
794 private function parseBlocks(array $context): array
795 {
796 /* Variable for debug only
797 errorDisplayed = false;
798 */
799
800 $blocks = [];
801
802 $templateLength = strlen($context['template']);
803 while ($context['position'] < $templateLength)
804 {
805 $blockStartPosition = $context['position'];
806 $context = $this->parseGroup($context);
807 if ($context['hasError'])
808 {
809 // Debug info
810 /*if (!$errorDisplayed)
811 {
812 echo str_replace(PHP_EOL, '<br />', htmlspecialcharsbx($this->getErrorsText($context)));
813 $errorDisplayed = true;
814 }*/
815
816 $errorInfo = $context['error']['info'];
817 if (!empty($errorInfo)
818 && is_array($errorInfo)
819 && isset($errorInfo['groupStartPosition'])
820 && $errorInfo['groupStartPosition'] > $blockStartPosition)
821 {
822 $blockLength = $errorInfo['groupStartPosition'] - $blockStartPosition + 1;
823 }
824 else
825 {
826 $blockLength = 1;
827 }
828
829 $this->appendTextBlock(
830 $blocks,
831 $context['error']['position'],
832 substr($context['template'], $blockStartPosition, $blockLength)
833 );
834 $context = $this->clearContextInfoAndError($context);
835 $context['position'] = $blockStartPosition + $blockLength;
836 }
837 else
838 {
839 $groupStartPosition = $context['info']['position'];
840 if ($groupStartPosition > $blockStartPosition)
841 {
842 $this->appendTextBlock(
843 $blocks,
844 $blockStartPosition,
845 substr(
846 $context['template'],
847 $blockStartPosition,
848 $groupStartPosition - $blockStartPosition
849 )
850 );
851 }
852
853 if ($context['info']['value'] !== '')
854 {
855 $this->appendGroupBlock(
856 $blocks,
857 $groupStartPosition,
858 $context['info']['value']
859 );
860 }
861
862 $context = $this->clearContextInfo($context);
863 }
864 }
865
866 if (!$context['hasError'])
867 {
868 $context['info'] = ['blocks' => $blocks];
869 }
870
871 return $context;
872 }
873
874 public function convert(Address $address): string
875 {
876 $result = '';
877
878 $context = $this->createContext();
879 $context['template'] = $this->template;
880 $context['address'] = $address;
881
882 $context = $this->parseBlocks($context);
883
884 if (!$context['hasError'])
885 {
886 foreach ($context['info']['blocks'] as $block)
887 {
888 if ($block['type'] === 'text')
889 {
890 $result .= $this->unescapeText($block['value']);
891 }
892 else
893 {
894 $result .= $block['value'];
895 }
896 }
897 }
898
899 if ($result !== '')
900 {
901 $result = explode(self::STR_DELIMITER_PLACEHOLDER, $result);
902 $result = array_values(
903 array_filter($result, function ($part) {
904 return ($part !== '');
905 })
906 );
907 if ($this->htmlEncode && !empty($result) && is_array($result))
908 {
909 array_walk($result, function (&$part) {
910 $part = htmlspecialcharsbx($part);
911 });
912 }
913
914 $result = implode($this->delimiter, $result);
915 }
916
917 return $result;
918 }
919}
__construct(string $template, string $delimiter, bool $htmlEncode, Format $format=null)
static isTypeExist(string $type)
Definition type.php:33