# elementor/4.3.0-beta3/modules/atomic-widgets/css-converter/variable-prop-value-transformer.php

Elementor Website Builder – more than just a page builder, version 4.3.0-beta3. 432 lines.

- Page: https://pluginprobe.com/plugins/elementor/4.3.0-beta3/code/modules/atomic-widgets/css-converter/variable-prop-value-transformer.php
- Raw: https://pluginprobe.com/plugins/elementor/4.3.0-beta3/raw/modules/atomic-widgets/css-converter/variable-prop-value-transformer.php
- Modified: 2026-09-01T11:47:36+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/elementor/4.3.0-beta3/code/modules/atomic-widgets/css-converter/variable-prop-value-transformer.php#L10-L20`.

```php
<?php

namespace Elementor\Modules\AtomicWidgets\CssConverter;

use Elementor\Modules\AtomicWidgets\CssConverter\Converter_Registry_Factory;
use Elementor\Modules\AtomicWidgets\PropTypes\Base\Array_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Base\Object_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Color_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Contracts\Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Font_Family_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Primitives\String_Prop_Type;
use Elementor\Modules\AtomicWidgets\PropTypes\Union_Prop_Type;
use Elementor\Modules\AtomicWidgets\Styles\Size_Constants;
use Elementor\Modules\Variables\Adapters\Prop_Type_Adapter;
use Elementor\Modules\Variables\PropTypes\Color_Variable_Prop_Type;
use Elementor\Modules\Variables\PropTypes\Size_Variable_Prop_Type;
use Elementor\Modules\Variables\Services\Variables_Service;
use Elementor\Modules\Variables\Utils\Variable_Type_Keys;

if ( ! defined( 'ABSPATH' ) ) {
	exit; // Exit if accessed directly.
}

class Variable_Prop_Value_Transformer {
	private Variables_Service $variables_service;

	public function __construct( Variables_Service $variables_service ) {
		$this->variables_service = $variables_service;
	}

	/**
	 * Resolves global-color-variable references inside gradient color stops to their actual color values.
	 * Variables cannot be used inside gradient stops because the gradient UI control does not support them.
	 * If a variable cannot be resolved, the entire background prop is ejected to custom CSS.
	 *
	 * @param array                                                                   $props  Mutated in place.
	 * @param array<int, array{property: string, value: string, declaration: string}> $rules
	 * @return string[] Extra custom CSS declarations produced by ejected props.
	 */
	public function normalize_gradient_color_stops( array &$props, array $rules ): array {
		$background = $props['background'] ?? null;

		if ( ! is_array( $background ) || 'background' !== ( $background['$$type'] ?? null ) ) {
			return [];
		}

		$overlays = $background['value']['background-overlay']['value'] ?? null;

		if ( ! is_array( $overlays ) ) {
			return [];
		}

		foreach ( $overlays as $i => $overlay ) {
			if ( 'background-gradient-overlay' !== ( $overlay['$$type'] ?? null ) ) {
				continue;
			}

			$stops = $overlay['value']['stops']['value'] ?? [];

			foreach ( $stops as $j => $stop ) {
				if ( 'color-stop' !== ( $stop['$$type'] ?? null ) ) {
					continue;
				}

				$color = $stop['value']['color'] ?? null;

				if ( ! is_array( $color ) || Color_Variable_Prop_Type::get_key() !== ( $color['$$type'] ?? null ) ) {
					continue;
				}

				$variable = $this->variables_service->find_by_label_or_id( $color['value'] ?? '' );

				if ( null === $variable || '' === ( $variable['value'] ?? '' ) ) {
					unset( $props['background'] );
					$declaration = $this->declaration_for_ejected_prop( $rules, 'background' );
					return null !== $declaration ? [ $declaration . ';' ] : [];
				}

				$props['background']['value']['background-overlay']['value'][ $i ]['value']['stops']['value'][ $j ]['value']['color'] = Color_Prop_Type::generate( $variable['value'] );
			}
		}

		return [];
	}

	/**
	 * @param array                                                                   $props
	 * @param array                                                                   $schema
	 * @param array<int, array{property: string, value: string, declaration: string}> $rules
	 * @return array{props: array, custom_css: string[], rejected: string[]}
	 */
	public function eject_unresolved_var_props( array $props, array $schema, array $rules ): array {
		$custom_css = [];
		$rejected = [];

		foreach ( $props as $key => $prop_value ) {
			if ( ! isset( $schema[ $key ] ) || ! ( $schema[ $key ] instanceof Prop_Type ) ) {
				continue;
			}

			$reference = $this->find_var_reference_in_value( $prop_value, $schema[ $key ] );

			if ( null === $reference ) {
				continue;
			}

			$variable = $this->variables_service->find_by_label_or_id( $reference );

			if (
				null !== $variable
				&& $this->variable_fits_prop_type( $schema[ $key ], $variable['type'] ?? '' )
			) {
				continue;
			}

			unset( $props[ $key ] );

			$declaration = $this->declaration_for_ejected_prop( $rules, $key );

			if ( null === $declaration ) {
				continue;
			}

			$declaration = $declaration . ';';

			if ( null === $variable ) {
				$custom_css[] = $declaration;
				continue;
			}

			$rejected[] = $declaration;
		}

		return [
			'props' => $props,
			'custom_css' => $custom_css,
			'rejected' => $rejected,
		];
	}

	public function transform( array $props, array $schema ): array {
		$transformed = [];

		foreach ( $props as $key => $prop_value ) {
			if ( ! isset( $schema[ $key ] ) || ! ( $schema[ $key ] instanceof Prop_Type ) ) {
				$transformed[ $key ] = $prop_value;
				continue;
			}

			$transformed[ $key ] = $this->transform_value( $prop_value, $schema[ $key ] );
		}

		return $transformed;
	}

	/**
	 * @param mixed $prop_value
	 * @return mixed
	 */
	private function transform_value( $prop_value, Prop_Type $prop_type ) {
		if ( ! is_array( $prop_value ) || ! isset( $prop_value['$$type'] ) ) {
			return $prop_value;
		}

		if ( $prop_type instanceof Union_Prop_Type ) {
			$promoted = $this->try_promote_to_variable( $prop_value, $prop_type );

			if ( null !== $promoted ) {
				return $promoted;
			}

			$branch = $prop_type->get_prop_type( $prop_value['$$type'] );

			if ( ! $branch ) {
				return $prop_value;
			}

			return $this->transform_value( $prop_value, $branch );
		}

		if ( $prop_type instanceof Object_Prop_Type ) {
			if ( ! is_array( $prop_value['value'] ?? null ) ) {
				return $prop_value;
			}

			$shape = $prop_type->get_shape();

			foreach ( $shape as $field => $field_type ) {
				if ( ! isset( $prop_value['value'][ $field ] ) ) {
					continue;
				}

				$prop_value['value'][ $field ] = $this->transform_value(
					$prop_value['value'][ $field ],
					$field_type
				);
			}

			return $prop_value;
		}

		if ( $prop_type instanceof Array_Prop_Type ) {
			if ( ! is_array( $prop_value['value'] ?? null ) ) {
				return $prop_value;
			}

			foreach ( $prop_value['value'] as $index => $item ) {
				$prop_value['value'][ $index ] = $this->transform_value(
					$item,
					$prop_type->get_item_type()
				);
			}

			return $prop_value;
		}

		return $prop_value;
	}

	private function try_promote_to_variable( array $prop_value, Union_Prop_Type $union ): ?array {
		$reference = $this->extract_var_reference( $prop_value );

		if ( null === $reference ) {
			return null;
		}

		$variable = $this->variables_service->find_by_label_or_id( $reference );

		if ( null === $variable ) {
			return null;
		}

		$variable_type = $this->resolve_variable_prop_type_key( $variable['type'] ?? '', $union );

		if ( null === $variable_type ) {
			return null;
		}

		$id = $variable['id'] ?? '';

		if ( '' === $id ) {
			return null;
		}

		return [
			'$$type' => $variable_type,
			'value' => $id,
		];
	}

	private function extract_var_reference( array $prop_value ): ?string {
		$type = $prop_value['$$type'] ?? '';
		$value = $prop_value['value'] ?? null;

		$string_backed_types = [
			Color_Prop_Type::get_key(),
			String_Prop_Type::get_key(),
			Font_Family_Prop_Type::get_key(),
		];

		if ( in_array( $type, $string_backed_types, true ) && is_string( $value ) ) {
			return Css_Var_Reference::parse( $value );
		}

		if ( 'size' === $type && is_array( $value ) ) {
			$unit = $value['unit'] ?? '';

			if ( Size_Constants::UNIT_CUSTOM !== $unit || ! is_string( $value['size'] ?? null ) ) {
				return null;
			}

			return Css_Var_Reference::parse( $value['size'] );
		}

		return null;
	}

	/**
	 * @param mixed $prop_value
	 */
	private function find_var_reference_in_value( $prop_value, Prop_Type $prop_type ): ?string {
		if ( ! is_array( $prop_value ) || ! isset( $prop_value['$$type'] ) ) {
			return null;
		}

		if ( Variable_Type_Keys::is_variable_type( $prop_value['$$type'] ) ) {
			return null;
		}

		if ( $prop_type instanceof Union_Prop_Type ) {
			$reference = $this->extract_var_reference( $prop_value );

			if ( null !== $reference ) {
				return $reference;
			}

			$branch = $prop_type->get_prop_type( $prop_value['$$type'] );

			if ( ! $branch ) {
				return null;
			}

			return $this->find_var_reference_in_value( $prop_value, $branch );
		}

		if ( $prop_type instanceof Object_Prop_Type ) {
			if ( ! is_array( $prop_value['value'] ?? null ) ) {
				return null;
			}

			foreach ( $prop_type->get_shape() as $field => $field_type ) {
				if ( ! isset( $prop_value['value'][ $field ] ) ) {
					continue;
				}

				$reference = $this->find_var_reference_in_value(
					$prop_value['value'][ $field ],
					$field_type
				);

				if ( null !== $reference ) {
					return $reference;
				}
			}

			return null;
		}

		if ( $prop_type instanceof Array_Prop_Type ) {
			if ( ! is_array( $prop_value['value'] ?? null ) ) {
				return null;
			}

			foreach ( $prop_value['value'] as $item ) {
				$reference = $this->find_var_reference_in_value(
					$item,
					$prop_type->get_item_type()
				);

				if ( null !== $reference ) {
					return $reference;
				}
			}

			return null;
		}

		return $this->extract_var_reference( $prop_value );
	}

	private function variable_fits_prop_type( Prop_Type $prop_type, string $variable_type ): bool {
		if ( '' === $variable_type ) {
			return false;
		}

		if ( $prop_type instanceof Union_Prop_Type ) {
			return null !== $this->resolve_variable_prop_type_key( $variable_type, $prop_type );
		}

		$resolved_type = Variable_Type_Keys::get_resolved_type( $variable_type );

		return null !== $resolved_type && $resolved_type === $prop_type::get_key();
	}

	/**
	 * @param array<int, array{property: string, value: string, declaration: string}> $rules
	 */
	private function declaration_for_ejected_prop( array $rules, string $property ): ?string {
		$declaration = $this->declaration_for_property( $rules, $property );

		if ( null !== $declaration ) {
			return $declaration;
		}

		$longhands_by_aggregate = [
			'background' => [
				'background-color',
				'background-clip',
				'background-image',
				'background-repeat',
				'background-attachment',
				'background-position',
				'background-size',
			],
			'padding' => array_keys( Converter_Registry_Factory::DIMENSIONS_SIDE_SPECS ),
			'margin'  => array_keys( Converter_Registry_Factory::DIMENSIONS_SIDE_SPECS ),
		];

		foreach ( $longhands_by_aggregate[ $property ] ?? [] as $longhand ) {
			$declaration = $this->declaration_for_property( $rules, $longhand );

			if ( null !== $declaration ) {
				return $declaration;
			}
		}

		return null;
	}

	/**
	 * @param array<int, array{property: string, value: string, declaration: string}> $rules
	 */
	private function declaration_for_property( array $rules, string $property ): ?string {
		foreach ( $rules as $rule ) {
			if ( $rule['property'] === $property ) {
				return $rule['declaration'];
			}
		}

		return null;
	}

	private function resolve_variable_prop_type_key( string $variable_type, Union_Prop_Type $union ): ?string {
		if ( '' === $variable_type ) {
			return null;
		}

		if ( $union->get_prop_type( $variable_type ) ) {
			return $variable_type;
		}

		if (
			Prop_Type_Adapter::GLOBAL_CUSTOM_SIZE_VARIABLE_KEY === $variable_type
			&& $union->get_prop_type( Size_Variable_Prop_Type::get_key() )
		) {
			return Size_Variable_Prop_Type::get_key();
		}

		return null;
	}
}

```
