Mojo::Util(3)

πŸŽ€ NAME πŸŽ€

Mojo::Util β€” Portable utility functions πŸ’…βœ¨

(aka the Mojo::Util(3) page from the User Contributed Perl Documentation β€” hiii, welcome, come sit with us 🫢)


🌸 SYNOPSIS 🌸

use Mojo::Util qw(b64_encode url_escape url_unescape);

my $str = 'test=23';
my $escaped = url_escape $str;
say url_unescape $escaped;
say b64_encode $escaped, '';

πŸ¦‹ DESCRIPTION πŸ¦‹

okay so Mojo::Util provides portable utility functions for Mojo. That's literally the whole vibe. It's giving Swiss Army knife but make it Perl. πŸ’–


✨ FUNCTIONS ✨

Mojo::Util implements the following functions, and β€” slay β€” you can import each one individually. No need to drag the whole friend group along if you only need one. πŸ‘‘

πŸ“ b64_decode

my $bytes = b64_decode $b64;

Base64 decode bytes with MIME::Base64. Turning your cute little b64 blob back into raw bytes, iconic. πŸ”₯

πŸ“ b64_encode

my $b64 = b64_encode $bytes;
my $b64 = b64_encode $bytes, "\n";

Base64 encode bytes with MIME::Base64. The line ending defaults to a newline, so if you don't pass one it's already handled for you. πŸŽ€

πŸ“ camelize

my $camelcase = camelize $snakecase;

Convert a "snake_case" string to "CamelCase" and replace "-" with "::". Glow-up transformation, honestly.

# "FooBar"
camelize 'foo_bar';

# "FooBar::Baz"
camelize 'foo_bar-baz';

# "FooBar::Baz"
camelize 'FooBar::Baz';

πŸ“ class_to_file

my $file = class_to_file 'Foo::Bar';

Convert a class name to a file. Lowkey essential.

# "foo_bar"
class_to_file 'Foo::Bar';

# "foobar"
class_to_file 'FOO::Bar';

# "foo_bar"
class_to_file 'FooBar';

# "foobar"
class_to_file 'FOOBar';

πŸ“ class_to_path

my $path = class_to_path 'Foo::Bar';

Convert class name to path, as used by %INC. Yes, that %INC.

# "Foo/Bar.pm"
class_to_path 'Foo::Bar';

# "FooBar.pm"
class_to_path 'FooBar';

πŸ“ decamelize

my $snakecase = decamelize $camelcase;

Convert a "CamelCase" string to "snake_case" and replace "::" with "-". The reverse makeover. πŸ’…

# "foo_bar"
decamelize 'FooBar';

# "foo_bar-baz"
decamelize 'FooBar::Baz';

# "foo_bar-baz"
decamelize 'foo_bar-baz';

πŸ“ decode

my $chars = decode 'UTF-8', $bytes;

Decode bytes to characters with Encode β€” or return "undef" if decoding failed. So like, check for undef, bestie, fr fr. πŸ™ˆ

my $value = decrypt_cookie $encrypted, 'passw0rd', 'salt';

Decrypt a cookie value encrypted with "encrypt_cookie". Returns the decrypted value or "undef". Spy-movie energy. πŸ•΅οΈ

πŸ“ deprecated

deprecated 'foo is DEPRECATED in favor of bar';

Warn about a deprecated feature from the perspective of the caller. And get this β€” you can also set the "MOJO_FATAL_DEPRECATIONS" environment variable to make them die instead with Carp. Dramatic? yes. Useful? also yes. πŸ’€

πŸ“ dumper

my $perl = dumper {some => 'data'};

Dump a Perl data structure with Data::Dumper. Look at your data, isn't it cute. πŸŽ€

πŸ“ encode

my $bytes = encode 'UTF-8', $chars;

Encode characters to bytes with Encode. The other direction of the decode situation.

my $encrypted = encrypt_cookie $value, 'passw0rd', 'salt';

Encrypt cookie value. That's the whole description, it's giving mysterious. πŸ”

πŸ“ extract_usage

my $usage = extract_usage;
my $usage = extract_usage '/home/sri/foo.pod';

Extract the usage message from the SYNOPSIS section of a file containing POD documentation. It defaults to using the file this function was called from β€” sneaky but iconic.

# "Usage: APPLICATION test [OPTIONS]\n"
extract_usage;

=head1 SYNOPSIS

  Usage: APPLICATION test [OPTIONS]

=cut

πŸ“ generate_secret

my $secret = generate_secret;

Generate a random secret with a cryptographically secure random number generator. Not your cousin's birthday, a real secret. πŸ”₯

πŸ“ getopt

getopt
  'H|headers=s' => \my @headers,
  't|timeout=i' => \my $timeout,
  'v|verbose'   => \my $verbose;
getopt $array,
  'H|headers=s' => \my @headers,
  't|timeout=i' => \my $timeout,
  'v|verbose'   => \my $verbose;
getopt $array, ['pass_through'],
  'H|headers=s' => \my @headers,
  't|timeout=i' => \my $timeout,
  'v|verbose'   => \my $verbose;

Extract options from an array reference with Getopt::Long, but without changing its global configuration β€” respectful king behavior. It defaults to using @ARGV. The configuration options "no_auto_abbrev" and "no_ignore_case" are enabled by default. πŸ’…

# Extract "charset" option
getopt ['--charset', 'UTF-8'], 'charset=s' => \my $charset;
say $charset;

πŸ“ gunzip

my $uncompressed = gunzip $compressed;

Uncompress bytes with IO::Compress::Gunzip. Un-squish. 🫧

πŸ“ gzip

my $compressed = gzip $uncompressed;

Compress bytes with IO::Compress::Gzip. Squish. πŸ—œοΈ

πŸ“ header_params

my ($params, $remainder) = header_params 'one=foo; two="bar", three=baz';

Extract HTTP header field parameters until the first comma according to RFC 5987 β€” link's right here: http://tools.ietf.org/html/rfc5987. Two return values, she's thorough. ✨

πŸ“ hmac_sha1_sum

my $checksum = hmac_sha1_sum $bytes, 'passw0rd';

Generate an HMAC-SHA1 checksum for bytes with Digest::SHA.

# "11cedfd5ec11adc0ec234466d8a0f2a83736aa68"
hmac_sha1_sum 'foo', 'passw0rd';

πŸ“ html_attr_unescape

my $str = html_attr_unescape $escaped;

Same as "html_unescape", but it handles the special rules from the HTML Living Standard (https://html.spec.whatwg.org) for HTML attributes. Fancy rules, fancy function.

# "foo=bar&ltest=baz"
html_attr_unescape 'foo=bar&ltest=baz';

# "foo=bar<est=baz"
html_attr_unescape 'foo=bar&lt;est=baz';

πŸ“ html_unescape

my $str = html_unescape $escaped;

Unescape all HTML entities in the string. Every single one, bestie.

# "<div>"
html_unescape '&lt;div&gt;';

πŸ“ humanize_bytes

my $str = humanize_bytes 1234;

Turn a number of bytes into a simplified human readable format. So your brain doesn't have to count zeros. 🧠✨

# "1B"
humanize_bytes 1;

# "7.5GiB"
humanize_bytes 8007188480;

# "13GiB"
humanize_bytes 13443399680;

# "-685MiB"
humanize_bytes -717946880;

πŸ“ md5_bytes

my $checksum = md5_bytes $bytes;

Generate a binary MD5 checksum for bytes with Digest::MD5.

πŸ“ md5_sum

my $checksum = md5_sum $bytes;

Generate an MD5 checksum for bytes with Digest::MD5. The hex-y one.

# "acbd18db4cc2f85cedef654fccc4a4d8"
md5_sum 'foo';

πŸ“ monkey_patch

monkey_patch $package, foo => sub {...};
monkey_patch $package, foo => sub {...}, bar => sub {...};

Monkey patch functions into a package. Chaos, but organized chaos. πŸ’

monkey_patch 'MyApp',
  one   => sub { say 'One!' },
  two   => sub { say 'Two!' },
  three => sub { say 'Three!' };

πŸ“ network_contains

my $bool = network_contains $network, $address;

Check that a given address is contained within a network in CIDR form. If the network is a single address, the addresses must be equivalent β€” no fuzzy matching, it's strict. πŸ’…

# True
network_contains('10.0.0.0/8', '10.10.10.10');
network_contains('10.10.10.10', '10.10.10.10');
network_contains('fc00::/7', 'fc::c0:ff:ee');

# False
network_contains('10.0.0.0/29', '10.10.10.10');
network_contains('10.10.10.12', '10.10.10.10');
network_contains('fc00::/7', '::1');

πŸ“ punycode_decode

my $str = punycode_decode $punycode;

Punycode decode a string as described in RFC 3492 β€” that's https://tools.ietf.org/html/rfc3492 for the record.

# "bΓΌcher"
punycode_decode 'bcher-kva';

πŸ“ punycode_encode

my $punycode = punycode_encode $str;

Punycode encode a string as described in RFC 3492 β€” https://tools.ietf.org/html/rfc3492. Yep, same RFC, opposite direction.

# "bcher-kva"
punycode_encode 'bΓΌcher';

πŸ“ quote

my $quoted = quote $str;

Quote string. Short, sweet, to the point. πŸŽ€

πŸ“ random_bytes

my $random = random_bytes($bytes);

Generate $bytes worth of high entropy randomness. Maximum chaos, cryptographically speaking. 🎲

πŸ“ scope_guard

my $guard = scope_guard sub {...};

Create an anonymous scope guard object that will execute the passed callback when the object is destroyed. Set it and forget it, then it fires at the end. 🀫

# Execute closure at end of scope
{
  my $guard = scope_guard sub { say "Mojo!" };
  say "Hello";
}

πŸ“ secure_compare

my $bool = secure_compare $str1, $str2;

A constant time comparison algorithm to prevent timing attacks. Important detail: the secret string should be the second argument, to avoid leaking information about the length of the string. Don't mix that up, bestie, fr fr. πŸ”

πŸ“ sha1_bytes

my $checksum = sha1_bytes $bytes;

Generate a binary SHA1 checksum for bytes with Digest::SHA.

πŸ“ sha1_sum

my $checksum = sha1_sum $bytes;

Generate a SHA1 checksum for bytes with Digest::SHA. The hex-y sibling of sha1_bytes.

# "0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33"
sha1_sum 'foo';

πŸ“ slugify

my $slug = slugify $string;
my $slug = slugify $string, $bool;

Returns a URL slug generated from the input string. So here's the tea: non-word characters are removed, the string is trimmed and lowercased, and whitespace characters are replaced by a dash. By default, non-ASCII characters are normalized to ASCII word characters or removed β€” but if you pass a true value as the second parameter, all word characters will be allowed in the result according to unicode semantics. She contains multitudes. 🌈

# "joel-is-a-slug"
slugify 'Joel is a slug';

# "this-is-my-resume"
slugify 'This is: my - rΓ©sumΓ©! β˜ƒ ';

# "this-is-my-rΓ©sumΓ©"
slugify 'This is: my - rΓ©sumΓ©! β˜ƒ ', 1;
my $tree = split_cookie_header 'a=b; expires=Thu, 07 Aug 2008 07:07:59 GMT';

Same as "split_header", but it handles "expires" values from RFC 6265 β€” https://tools.ietf.org/html/rfc6265. Cookies stay winning. πŸͺ

πŸ“ split_header

my $tree = split_header 'foo="bar baz"; test=123, yada';

Split an HTTP header value into key/value pairs. Each comma separated part gets its own array reference, and keys without a value get "undef" assigned. Yes, undef, don't @ me.

# "one"
split_header('one; two="three four", five=six')->[0][0];

# "two"
split_header('one; two="three four", five=six')->[0][2];

# "three four"
split_header('one; two="three four", five=six')->[0][3];

# "five"
split_header('one; two="three four", five=six')->[1][0];

# "six"
split_header('one; two="three four", five=six')->[1][1];

πŸ“ steady_time

my $time = steady_time;

High resolution time elapsed from an arbitrary fixed point in the past, resilient to time jumps if a monotonic clock is available through Time::HiRes. So like, time that refuses to be gaslit by clock changes. β°πŸ’…

πŸ“ tablify

my $table = tablify [['foo', 'bar'], ['baz', 'yada']];

Row-oriented generator for text tables. Little ASCII picnic table for your data. 🧺

# "foo   bar\nyada  yada\nbaz   yada\n"
tablify [['foo', 'bar'], ['yada', 'yada'], ['baz', 'yada']];

πŸ“ term_escape

my $escaped = term_escape $str;

Escape all POSIX control characters except for "\n". Note the exception, bestie β€” the newline stays, everything else gets esc-aped. πŸ‘€

# "foo\\x09bar\\x0d\n"
term_escape "foo\tbar\r\n";

πŸ“ trim

my $trimmed = trim $str;

Trim whitespace characters from both ends of the string. Not one end. Both. Symmetry queen. βœ‚οΈ

# "foo bar"
trim '  foo bar  ';

πŸ“ unindent

my $unindented = unindent $str;

Unindent a multi-line string. Dedenting but make it a function.

# "foo\nbar\nbaz\n"
unindent "  foo\n  bar\n  baz\n";

πŸ“ unquote

my $str = unquote $quoted;

Unquote string. The quote function's other half. 🫢

πŸ“ url_escape

my $escaped = url_escape $str;
my $escaped = url_escape $str, '^A-Za-z0-9\-._~';

Percent encode unsafe characters in the string as described in RFC 3986 β€” https://tools.ietf.org/html/rfc3986. The pattern used defaults to "^A-Za-z0-9\-._~", so that's what's getting let through unless you say otherwise.

# "foo%3Bbar"
url_escape 'foo;bar';

πŸ“ url_unescape

my $str = url_unescape $escaped;

Decode percent encoded characters in the string as described in RFC 3986 β€” https://tools.ietf.org/html/rfc3986. The reverse slay of url_escape. 🌸

# "foo;bar"
url_unescape 'foo%3Bbar';

Like, <cyan>boom</cyan> β€” the %3B was hiding a semicolon the whole time and now it's out here living its truth. πŸ¦‹ That's the entire arc of url_unescape, bestie, no notes.


✨ xml_escape ✨

my $escaped = xml_escape $str;

Okay so this one is your little XML bodyguard 🫢 β€” it escapes the unsafe characters &, <, >, " and ' in a string so they can't wreak havoc. Very protective, very demure. But <hotpink>important plot twist</hotpink>: it does not escape Mojo::ByteStream objects. Those are already built different and get left exactly as they are, periodt.

# "&lt;div&gt;"
xml_escape '<div>';

See how the angle brackets got all shy and turned into &lt; / &gt;? Iconic behaviour honestly. πŸ’…

# "<div>"
use Mojo::ByteStream qw(b);
xml_escape b('<div>');

And here β€” a Mojo::ByteStream (that's the b(...) wrapper, iykyk) rolls up and comes back out completely untouched as <div>. Not a single character changed. Main character energy, the escaping simply does not apply to it. πŸ’€


🌈 xor_encode 🌈

my $encoded = xor_encode $str, $key;

XOR encodes a string using a key of variable length. That's the whole thing β€” your string goes in, gets XOR'd against $key (and the key can literally be any length, no rules, no gatekeeping ✨), and the encoded result comes back out. Very "we do a little cryptography" of it. πŸ”₯


πŸ’œ SEE ALSO πŸ’œ

Go read those, bestie, they're the moment. πŸ‘‘


fr fr though β€” that's genuinely the end of the page, nothing got skipped, every flag and example is present and accounted for πŸŽ€

okay and that's a wrap on this one 🌸 this page is from perl v5.42.0, dated 2026-07-15, so if your install says something else, you're on a different timeline, bestie β€” check your version and stay hydrated πŸ’–βœ¨

πŸ“¦ where this page comes from