Files
roi-theme/wp-content/plugins/thrive-product-manager/thrive-dashboard/inc/webhooks/class-td-webhooks-templating.php
root a22573bf0b Commit inicial - WordPress Análisis de Precios Unitarios
- WordPress core y plugins
- Tema Twenty Twenty-Four configurado
- Plugin allow-unfiltered-html.php simplificado
- .gitignore configurado para excluir wp-config.php y uploads

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-03 21:04:30 -06:00

199 lines
6.1 KiB
PHP
Executable File

<?php
/**
* Thrive Themes - https://thrivethemes.com
*
* @package thrive-dashboard
*/
namespace TVE\Dashboard\Webhooks;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Silence is golden!
}
/**
* Simple templating and payload shaping for webhook requests.
*/
class TD_Webhooks_Templating {
/**
* JSON encode options: no escaped slashes or unicode.
*
* @var int
*/
const JSON_OPTIONS = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
/**
* Build request payload from mapping and context
*
* @param array $mapping [ [ 'key' => 'user[name]', 'value' => '{{data.name}}' ], ... ]
* @param array $context Context containing 'data', 'user', etc.
*
* @return array
*/
public static function build_payload( array $mapping, array $context ): array {
$fields = [];
foreach ( $mapping as $field ) {
// If the key is not set or is empty, skip.
if ( empty( $field['key'] ) ) {
continue;
}
$original_key = str_replace( ']', '', (string) $field['key'] );
$reference = &$fields;
// Navigate through the fields using the original key.
foreach ( explode( '[', $original_key ) as $key ) {
// If the key is empty, skip.
if ( $key === '' ) {
continue;
}
// If the key does not exist, create an empty array.
if ( ! array_key_exists( $key, $reference ) ) {
$reference[ $key ] = [];
}
$reference = &$reference[ $key ];
}
$value = $field['value'] ?? '';
// If value starts and ends with %, set it to empty.
// %FIELD% is used by automator, we are not using it.
if ( is_string( $value ) && preg_match( '/^%.*%$/', $value ) ) {
$value = '';
}
// If value is a single placeholder, resolve raw so arrays remain arrays (for checkboxes / multi-select)
if ( is_string( $value ) && preg_match( '/^\s*\{\{\s*([^}]+)\s*\}\}\s*$/', $value, $matches ) ) {
$value = self::get_by_path( $context, trim( $matches[1] ) ) ?? '';
} else {
// Resolve the placeholders.
$value = self::resolve_placeholders( $value, $context );
}
$reference = $value;
unset( $reference );
}
return $fields;
}
/**
* Resolve {{path}} placeholders using dot-notation into the provided context.
* If value is not a string, return as-is.
*
* @param mixed $value
* @param array $context
*
* @return mixed
*/
public static function resolve_placeholders( $value, array $context ) {
if ( ! is_string( $value ) ) {
return $value;
}
// Replace {{ path }} placeholders using a callback
$callback = function( $matches ) use ( &$context ) {
return self::placeholder_replace_callback( $matches, $context );
};
return preg_replace_callback( '/\{\{\s*([^}]+)\s*\}\}/', $callback, $value );
}
/**
* Callback used by resolve_placeholders to replace a single {{ path }} match.
*
* @param array $matches
* @param array $context
* @return string
*/
private static function placeholder_replace_callback( array $matches, array $context ): string {
$path = trim( $matches[1] ?? '' );
$found = self::get_by_path( $context, $path );
if ( $found === null ) {
return '';
}
if ( is_scalar( $found ) ) {
return (string) $found;
}
return self::json_encode( $found );
}
/**
* Get value from nested array/object using dot notation (e.g., data.email, user.email)
*
* @param array $source
* @param string $path
*
* @return mixed|null
*/
public static function get_by_path( array $source, string $path ) {
// Split the dot-notation path into sanitized, non-empty segments
// a.b.c -> [ 'a', 'b', 'c' ]
$segments = array_filter( array_map( 'trim', explode( '.', $path ) ), static function( $s ) { return $s !== ''; } );
$cursor = $source;
foreach ( $segments as $segment ) {
if ( is_array( $cursor ) ) {
$resolved = self::resolve_array_segment( $cursor, $segment );
if ( $resolved === null ) {
return null;
}
$cursor = $resolved;
} elseif ( is_object( $cursor ) && isset( $cursor->$segment ) ) {
$cursor = $cursor->$segment;
} else {
return null;
}
}
// Return the resolved value after traversing the full path
return $cursor;
}
/**
* Attempt to resolve an array segment, accounting for PHP checkbox name conventions (foo vs foo[]).
*
* @param array $cursor Current array level being traversed
* @param string $seg Segment name
*
* @return mixed|null Returns the resolved value or null if not found
*/
private static function resolve_array_segment( array $cursor, string $seg ) {
if ( array_key_exists( $seg, $cursor ) ) {
return $cursor[ $seg ];
}
// Normalize checkbox-style names: allow foo[] vs foo
$alt = ( substr( $seg, -2 ) === '[]' ) ? substr( $seg, 0, -2 ) : ( $seg . '[]' );
// If the alternate checkbox-style key exists, prefer it
if ( array_key_exists( $alt, $cursor ) ) {
return $cursor[ $alt ];
}
return null;
}
/**
* JSON encode data with webhook-friendly options.
*
* Encodes without escaping slashes or unicode characters,
* making URLs and international text more readable.
*
* @param mixed $data Data to encode
* @return string JSON string
*/
public static function json_encode( $data ): string {
$json = wp_json_encode( $data, self::JSON_OPTIONS );
return ($json === false) ? '' : $json;
}
}