Rusty string formatting in PHP
As a PHP developer who likes to occasionally dabble in Rust, I find myself missing the developer experience of the format!()
family of macros when I come back to writing PHP.
If you're not familiar with format!()
or print!()
macros in Rust, they're essentially the equivalent of PHP's sprintf()
and printf()
. The main difference between the two languages is that Rust's macros don't inherit the syntax of C's sprintf()
where you have placeholders like %s
, %d
, etc. Instead you use {}
as the placeholders and Rust will just convert the values to strings.
Here's an example of what it looks like:
fn main() {
let name = "Ryan";
println!("Hello, {}!", name);
}
So I've built a new package for PHP that offers the same sort of syntax – let me introduce you to f
. The name f
was inspired (read "stolen") from Python, where "f-strings" are used to handle interpolation and inline formatting.
You can install the package using Composer:
composer require ryangjchandler/f
It provides a global f()
function that you can use to format a string.
$name = "Ryan";
echo f("Hello, {}!", $name);
That's pretty close, right? Of course, that's a really simple and silly example because you could just use regular interpolation. Here are some comparisons between f()
syntax and the equivalent sprintf()
syntax provided by PHP.
Referencing positional arguments
f(
"The {1} contains {0} monkeys. That's a nice {1} full of {0} monkeys.",
100,
'zoo',
)
f(
"The %2$s contains %1$d monkeys. That's a nice %2$s full of %1$d monkeys.",
100,
'zoo',
)
Note that F uses 0-based indexing when referencing positional arguments.
Right-justifying text
f(
"The following number has been padded to a length of 10 – {:0>10}.",
100,
)
// 0000000100
f(
"The following number has been padded to a length of 10 – %'010d.",
100,
)
// 0000000100
The sprintf()
syntax here is kind of mystical. '0'
is character to pad with, 10
is the length, d
is the format specifier.
F's syntax on the otherhand reads slightly nicer in my opinion. Use 0
to right-justify (>
) the placeholder to a length of 10
.
If you wanted to left-justify the text, you'd flip the operator to <
and it would just work. To left-justify with sprintf
, you'd need to add a -
character.
Escaping placeholders
f("I don't want the \{} placeholder to be formatted.")
f("I don't want the %%s placeholder to be formatted")
\
is the character that we're all used to using for escaping things, so it's natural to use that for F.
F supports a bunch of different placeholder formats. Here's the table of syntaxes at the time of writing this post.
Placeholder | Description |
---|---|
{} |
References an argument based on the position of the placeholder in the format string. |
{2} |
References the 3rd argument (0-based indexing). |
{name} |
References a named argument. |
{:b} |
Formats an integer as binary. |
{:x} |
Formats an integer as hexadecimal. |
{:o} |
Formats an integer as octal. |
{:>10} |
Right-justifies the argument to a width of 10 using a single space. |
{:<10} |
Left-justifies the argument to a width of 10 using a single space. |
{:0>10} |
Right-justifies the argument to a width of 10 using a custom character 0 . |
{:0<10} |
Left-justifies the argument to a width of 10 using a custom character 0 . |
{:>width$} |
Right-justifies the argument to a width defined by the named argument width . |
Sign off
Some people prefer string concatenation. Others like using sprintf()
. Me? I like the "rusty" syntax.
Check out the code on GitHub and maybe try it out in your next hobby project!