Automattic\WooCommerce\EmailEditor\Engine
Personalizer::personalize_href_tokens
Replace personalization tag tokens embedded in a link URL.
Method of the class: Personalizer{}
No Hooks.
Returns
string|null. The href with tokens replaced, or null when nothing was replaced.
Usage
// private - for code of main (parent) class only $result = $this->personalize_href_tokens( $href ): ?string;
- $href(string) (required)
- The href attribute value.
Personalizer::personalize_href_tokens() Personalizer::personalize href tokens code WC 11.1.2
private function personalize_href_tokens( string $href ): ?string {
// Decode both URL encoding (%XX) and HTML entities (') to handle various encoding scenarios.
$decoded_href = html_entity_decode( urldecode( $href ), ENT_QUOTES, 'UTF-8' );
if ( ! preg_match_all( '/\[' . self::TAG_NAME_PATTERN . '(?:\s+[^\]]+)?\]/', $decoded_href, $matches ) ) {
return null;
}
// Resolve every replaceable token first.
$replacements = array();
foreach ( array_unique( $matches[0] ) as $token_string ) {
$token = $this->parse_token( $token_string );
$tag = $this->tags_registry->get_by_token( $token['token'] );
if ( ! $tag ) {
continue;
}
$value = $tag->execute_callback( $this->get_callback_context( self::RENDERING_CONTEXT_HREF ), $token['arguments'] );
if ( '' !== $value ) {
$replacements[ $token_string ] = $value;
}
}
if ( ! $replacements ) {
return null;
}
// Prefer the original attribute value as the replacement base so legitimate
// percent-encoding in the surrounding URL is preserved; fall back to the decoded
// form when a replaced token occurrence exists only there (e.g. URL-encoded tokens).
// Only tokens that are actually replaced matter here — an unregistered bracket
// sequence that exists purely in the decoded form must not force the decoded base.
// Known tradeoff: the base is chosen for the whole href, so when the decoded form
// is used, unrelated percent-encoding elsewhere in the URL is decoded too.
$base = $href;
foreach ( array_keys( $replacements ) as $token_string ) {
if ( substr_count( $href, $token_string ) !== substr_count( $decoded_href, $token_string ) ) {
$base = $decoded_href;
break;
}
}
// The editor forces a protocol prefix when a tag is used as the whole URL
// ("http://[tag]"). Strip it only when the token directly after it is being
// replaced, so the tag value is used as-is while any suffix (e.g. appended
// query parameters) is kept.
if ( preg_match( '#^https?://(\[' . self::TAG_NAME_PATTERN . '(?:\s+[^\]]+)?\])#i', $base, $prefix_match ) && isset( $replacements[ $prefix_match[1] ] ) ) {
$base = (string) preg_replace( '#^https?://#i', '', $base );
}
// Single-pass replacement — tag values are never re-scanned for other tokens,
// and a regex replacement would interpret `$` and `\` in them.
return strtr( $base, $replacements );
}