Skip to content

User defined REVERSE() function Unicode support 🎉#453

Merged
JanJakes merged 3 commits into
WordPress:trunkfrom
teizz:trunk
Jul 17, 2026
Merged

User defined REVERSE() function Unicode support 🎉#453
JanJakes merged 3 commits into
WordPress:trunkfrom
teizz:trunk

Conversation

@teizz

@teizz teizz commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

As discussed with @JanJakes in pull request #434 I've gone ahead and added the code to support unicode characters while keeping behavior aligned with how mysql reverses a utf8mb4 unicode string, i.e. it correctly reverses multi-byte characters, but fails to preserve the correct order for grapheme clusters / combining characters.

As a practical example: both this implementation and mysql's REVERSE() function will reverse '🧟 🧟‍♂️ 🧟‍♀️' to become '♀‍🧟 ️♂‍🧟 🧟' instead of '🧟‍♀️ 🧟‍♂️ 🧟'.

I've also gone ahead and added a check to enable a fast path with strrev() in case all characters are normal ASCII, this improves both speed and prevents additional allocation for arrays.

Finally, I checked to see that

    preg_match_all( '/./u', $str, $matches );
    return implode( '', array_reverse( $matches[0] ) );

is actually about twice as fast as

    $chars = preg_split( '//u', $str, -1, PREG_SPLIT_NO_EMPTY );
    return implode( '', array_reverse( $chars ) );

@JanJakes JanJakes left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for working on this! I have some small suggestions and nitpicks. Otherwise, looks good!

return null;
}
if ( preg_match( '/[^\x00-\x7F]/', $str ) ) {
preg_match_all( '/./u', $str, $matches );

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let’s also add the s modifier so that . matches line feeds, and a test for that. Something like REVERSE('Hello\nWörld') should give dlröW\nolleH.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, and let's make sure that when the string has invalid UTF-8, we fallback to strrev. It would then become sth like this:

if (
	preg_match( '/[^\x00-\x7F]/', $str )
	&& preg_match_all( '/./us', $str, $matches )
) {
	return implode( '', array_reverse( $matches[0] ) );
}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that, it wasn't breaking until I checked the actual output and noticed it was silently dropping the newlines and just sticking lines together. And invalid utf8 / raw bytes returned nothing 😐. I've added your suggestions.

* Method to emulate MySQL REVERSE() function.
*
* Takes a string and returns the reverse of it.
* Compatible with MySQL behaviour for utf8mb4 strings.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this also applies to utf8mb3 so maybe let's just say sth like this:

* Reverse UTF-8 text by code point, matching MySQL behavior.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. I babble sometimes I guess 😉.

@teizz

teizz commented Jul 17, 2026

Copy link
Copy Markdown
Contributor Author

No worries @JanJakes, least I can do for using all of this.

I've also taken the liberty of adding a check to see if mb_str_split() is available, in which case that is faster than preg_match_all(). But since it doesn't fail on invalid utf-8 (it reverses some multi-bytes, but not all), I also check to see if the encoding is valid utf-8 and if not still fall back to strrev(). That way behavior is consistent between installations with PHP 7.4 and up and those below.

The optimization offers little practical benefit for typical values while duplicating UTF-8 reversal across environments. Keep the PCRE implementation consistent across supported PHP versions and avoid dependence on mb_internal_encoding().

@JanJakes JanJakes left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@teizz Thank you! I removed the mbstring part because it seems to be only modestly faster, and it adds more complexity (there was already a subtle bug in that mb_str_split didn't pass the UTF-8 parameter, and that would make it use mb_internal_encoding() instead). Otherwise all good, thanks!

@JanJakes
JanJakes merged commit 1c68310 into WordPress:trunk Jul 17, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants