• File: subtree-builder.php
  • Full Path: /home/slvgwrnz/public_html/wp-content/plugins/elementor/modules/mcp/abilities/build-composition/subtree-builder.php
  • Date Modified: 11/05/2026 9:17 AM
  • File size: 3.09 KB
  • MIME-type: text/x-php
  • Charset: utf-8
<?php

namespace Elementor\Modules\Mcp\Abilities\Build_Composition;

use Elementor\Modules\Mcp\Abilities\Appliers\V3_Node_Bridge;
use Elementor\Modules\Mcp\Abilities\Utils\Editor_Settings;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

class Subtree_Builder {

	private Xml_Parser $xml_parser;

	public function __construct( Xml_Parser $xml_parser ) {
		$this->xml_parser = $xml_parser;
	}

	/**
	 * @param \DOMDocument         $dom            XML document.
	 * @param array<string, array> $widget_configs Resolved type configs indexed by tag.
	 * @return array[]
	 */
	public function build( \DOMDocument $dom, array $widget_configs ): array {
		$root = $this->xml_parser->get_root( $dom );
		if ( ! $root ) {
			return [];
		}

		$subtrees = [];
		foreach ( $this->xml_parser->get_child_elements( $root ) as $child ) {
			$subtrees[] = $this->build_subtree( $child, $widget_configs );
		}

		return $subtrees;
	}

	/**
	 * Build an index of configuration-id -> reference to subtree node.
	 *
	 * @param array[]      $subtrees Built subtrees (mutated by reference downstream).
	 * @param \DOMDocument $dom      Source DOM.
	 * @return array<string, array&>
	 */
	public function index_by_config_id( array &$subtrees, \DOMDocument $dom ): array {
		$root = $this->xml_parser->get_root( $dom );
		if ( ! $root ) {
			return [];
		}

		$dom_roots = $this->xml_parser->get_child_elements( $root );
		$index = [];

		foreach ( $subtrees as $i => &$subtree ) {
			if ( isset( $dom_roots[ $i ] ) ) {
				$this->index_recursive( $subtree, $dom_roots[ $i ], $index );
			}
		}
		unset( $subtree );

		return $index;
	}

	private function build_subtree( \DOMElement $node, array $widget_configs ): array {
		$config = $widget_configs[ $this->xml_parser->get_tag_name( $node ) ];

		$children = [];
		foreach ( $this->xml_parser->get_child_elements( $node ) as $child ) {
			$children[] = $this->build_subtree( $child, $widget_configs );
		}

		$element = [
			'elType' => $config['elType'],
			'settings' => [],
			'elements' => $children,
			'editor_settings' => [],
		];

		if ( ! empty( $config['widgetType'] ) ) {
			$element['widgetType'] = $config['widgetType'];
		}

		$configuration_id = $this->xml_parser->get_configuration_id( $node );
		if ( null !== $configuration_id ) {
			Editor_Settings::apply_name( $element, $configuration_id );
		}

		if ( ! empty( $config['controls'] ) && V3_Node_Bridge::is_v3_node( $element ) ) {
			V3_Node_Bridge::seed_dynamic_defaults( $element, $config['controls'] );
		}

		return $element;
	}

	private function index_recursive( array &$subtree, \DOMElement $node, array &$index ): void {
		$configuration_id = $this->xml_parser->get_configuration_id( $node );
		if ( null !== $configuration_id ) {
			$index[ $configuration_id ] = &$subtree;
		}

		if ( ! isset( $subtree['elements'] ) || ! is_array( $subtree['elements'] ) ) {
			return;
		}

		$child_elements = $this->xml_parser->get_child_elements( $node );
		foreach ( $subtree['elements'] as $i => &$child_subtree ) {
			if ( isset( $child_elements[ $i ] ) ) {
				$this->index_recursive( $child_subtree, $child_elements[ $i ], $index );
			}
		}
		unset( $child_subtree );
	}
}