$variables Values to substitute. */ public static function render( string $template, array $variables ): string { $replacements = []; foreach ( $variables as $name => $value ) { $replacements[ '{' . $name . '}' ] = (string) $value; } $rendered = strtr( $template, $replacements ); // Remove any token that had no value. $rendered = (string) preg_replace( '/\{[a-z0-9_]+\}/i', '', $rendered ); // Collapse the blank lines that removing a token can leave behind, so a // message never goes out with a gap in the middle of it. $rendered = (string) preg_replace( "/[ \t]+\n/", "\n", $rendered ); $rendered = (string) preg_replace( "/\n{3,}/", "\n\n", $rendered ); return trim( $rendered ); } /** * Which tokens a template actually uses. * * Used by the editor to warn about a token the event does not provide — * catching the mistake while someone is looking at the form rather than after * a thousand messages have gone out with a hole in them. * * @param string $template Message body. * * @return list */ public static function tokensIn( string $template ): array { preg_match_all( '/\{([a-z0-9_]+)\}/i', $template, $matches ); return array_values( array_unique( $matches[1] ?? [] ) ); } /** * Tokens used by a template that the event does not offer. * * @param string $template Message body. * @param array $available Known variables. * * @return list */ public static function unknownTokens( string $template, array $available ): array { return array_values( array_diff( self::tokensIn( $template ), array_keys( $available ) ) ); } }