$tags Replacements keyed by tag name. */ public function __construct( private array $tags = array() ) {} /** * Adds a replacement. * * @param string $tag Tag name without braces. * @param string $value Replacement value. */ public function add( string $tag, string $value ): void { $this->tags[ $tag ] = $value; } /** * Returns a copy carrying one extra replacement. * * The alternative — `add()` on a shared instance — makes the result depend on * the order the callers happen to run in. `{all_fields}` is the case that bit: * the HTML body registered the markup table, the plain-text body immediately * replaced it with the text one, and between the admin notification and the * auto-reply the object still carried the previous message's table. Building a * body from a copy means nothing a body does can be seen by anything else. * * @param string $tag Tag name without braces. * @param string $value Replacement value. */ public function with( string $tag, string $value ): MergeTags { $copy = clone $this; $copy->add( $tag, $value ); return $copy; } /** * Returns the registered replacements. * * @return array Replacements keyed by tag name. */ public function all(): array { /** * Filters the merge tags available to a notification. * * @since 1.0.0 * * @param array $tags Replacements keyed by tag name. */ return (array) apply_filters( 'formpilot_merge_tags', $this->tags ); } /** * Replaces every known tag in a string. * * Unknown tags are left untouched, so a typo is visible rather than silently * turning into an empty string. * * @param string $text Text containing `{tag}` placeholders. */ public function replace( string $text ): string { if ( '' === $text || ! str_contains( $text, '{' ) ) { return $text; } $search = array(); $replace = array(); foreach ( $this->all() as $tag => $value ) { $search[] = '{' . $tag . '}'; $replace[] = $value; } return str_replace( $search, $replace, $text ); } /** * Returns the value of a single tag. * * @param string $tag Tag name without braces. * @param string $fallback Optional. Value returned when the tag is unknown. Default ''. */ public function get( string $tag, string $fallback = '' ): string { $tags = $this->all(); return isset( $tags[ $tag ] ) ? (string) $tags[ $tag ] : $fallback; } }