PluginProbe
Elementor Website Builder – more than just a page builder / 4.3.0-beta3
Elementor Website Builder – more than just a page builder v4.3.0-beta3
4.3.0-beta3 4.3.0-beta2 4.3.0-beta1 4.2.4 4.2.3 4.2.2 4.2.1 4.2.0 4.1.5 4.2.0-beta2 4.2.0-dev2 4.2.0-beta1 4.1.4 4.1.3 4.1.2 4.1.1 4.1.0 4.1.0-beta3 4.1.0-dev3 4.0.9 4.1.0-beta2 4.1.0-dev2 4.0.8 4.1.0-beta1 4.1.0-dev1 All 452 releases
elementor / modules / mcp / abilities / build-composition / xml-parser.php

xml-parser.php in Elementor Website Builder – more than just a page builder 4.3.0-beta3, at modules/mcp/abilities/build-composition/xml-parser.php

143 lines 3.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Elementor\Modules\Mcp\Abilities\Build_Composition;
4
5 if ( ! defined( 'ABSPATH' ) ) {
6 exit;
7 }
8
9 class Xml_Parser {
10
11 const CONFIGURATION_ID_ATTRIBUTE = 'configuration-id';
12 const COMPOSITION_ROOT_TAG = 'composition-root';
13
14 public function parse( string $xml_structure ) {
15 $wrapped = '<' . self::COMPOSITION_ROOT_TAG . '>' . $xml_structure . '</' . self::COMPOSITION_ROOT_TAG . '>';
16
17 $previous = libxml_use_internal_errors( true );
18 $dom = new \DOMDocument();
19 $loaded = $dom->loadXML( $wrapped, LIBXML_NONET | LIBXML_NOENT );
20 $errors = libxml_get_errors();
21 libxml_clear_errors();
22 libxml_use_internal_errors( $previous );
23
24 if ( ! $loaded ) {
25 $message = $errors ? $errors[0]->message : 'Unknown XML error.';
26 return new \WP_Error(
27 'invalid_xml',
28 /* translators: %s: XML parse error message */
29 sprintf( __( 'Failed to parse xml_structure: %s', 'elementor' ), trim( $message ) ),
30 [ 'status' => \WP_Http::BAD_REQUEST ]
31 );
32 }
33
34 return $dom;
35 }
36
37 public function get_root( \DOMDocument $dom ): ?\DOMElement {
38 return $dom->documentElement ?? null; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- DOMDocument API.
39 }
40
41 /**
42 * @return \DOMElement[]
43 */
44 public function get_child_elements( \DOMElement $node ): array {
45 $children = [];
46 // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- DOMElement API.
47 foreach ( $node->childNodes as $child ) {
48 if ( $child instanceof \DOMElement ) {
49 $children[] = $child;
50 }
51 }
52 return $children;
53 }
54
55 public function get_tag_name( \DOMElement $node ): string {
56 return $node->tagName; // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- DOMElement API.
57 }
58
59 public function get_configuration_id( \DOMElement $node ): ?string {
60 return $node->hasAttribute( self::CONFIGURATION_ID_ATTRIBUTE )
61 ? $node->getAttribute( self::CONFIGURATION_ID_ATTRIBUTE )
62 : null;
63 }
64
65 public function serialize_children( \DOMDocument $dom ): string {
66 $root = $this->get_root( $dom );
67 if ( ! $root ) {
68 return '';
69 }
70
71 $serialized = '';
72 foreach ( $this->get_child_elements( $root ) as $child ) {
73 $serialized .= $dom->saveXML( $child );
74 }
75 return $serialized;
76 }
77
78 /**
79 * @return \DOMElement[]
80 */
81 public function iterate_all_descendants( \DOMDocument $dom ): array {
82 $root = $this->get_root( $dom );
83 if ( ! $root ) {
84 return [];
85 }
86
87 $descendants = [];
88 $xpath = new \DOMXPath( $dom );
89 foreach ( $xpath->query( './/*', $root ) as $node ) {
90 if ( $node instanceof \DOMElement ) {
91 $descendants[] = $node;
92 }
93 }
94 return $descendants;
95 }
96
97 public function collect_duplicate_configuration_id_errors( \DOMDocument $dom ): array {
98 $counts = [];
99
100 foreach ( $this->iterate_all_descendants( $dom ) as $node ) {
101 $configuration_id = $this->get_configuration_id( $node );
102 if ( null === $configuration_id || '' === $configuration_id ) {
103 continue;
104 }
105
106 if ( ! isset( $counts[ $configuration_id ] ) ) {
107 $counts[ $configuration_id ] = 0;
108 }
109
110 $counts[ $configuration_id ]++;
111 }
112
113 $errors = [];
114 foreach ( $counts as $configuration_id => $count ) {
115 if ( $count <= 1 ) {
116 continue;
117 }
118
119 $errors[] = sprintf(
120 /* translators: %s: duplicate configuration-id value */
121 __( 'Duplicate configuration-id "%s". Each element must have a unique configuration-id.', 'elementor' ),
122 $configuration_id
123 );
124 }
125
126 return $errors;
127 }
128
129 public function validate_unique_configuration_ids( \DOMDocument $dom ): ?\WP_Error {
130 $errors = $this->collect_duplicate_configuration_id_errors( $dom );
131
132 if ( empty( $errors ) ) {
133 return null;
134 }
135
136 return new \WP_Error(
137 'elementor_duplicate_configuration_id',
138 implode( ' ', $errors ),
139 [ 'status' => \WP_Http::BAD_REQUEST ]
140 );
141 }
142 }
143