I much prefer join over sprintf (as well as over here-docs or multi-line quoted strings or concatenation). That preference took a lot of years to form but has now lasted almost as many years. Other techniques don't scale nearly so well, IME, in the face of on-going maintenance and growing size and complexity of the string and programmers' potential for simple mistakes.
In the rare case of using a Perl here-doc as a quick solution, I also move it to be top-level. I also have a somewhat unusual editor configuration that includes showing trailing whitespace (something a significant percentage of my coworkers don't appear to have -- based on them seeming oblivious to the trailing whitespace that they sometimes commit). And even those here-docs rarely last very long.
Heck, even in the short term I'd likely just replace it with a multi-line quoted string:
( my $string= q{
...
} ) =~ s/(?=^|(\n))\n[^\S\n]+/defined $1 ? "\n" : ''/ge;
(Clever undenting regex included just to show how easy it is to eliminate the opening newline that this adds -- not because I find myself actually using such.)
Having here-docs at the top level is also annoying. Not as annoying as not being able to re-indent code w/o breaking it, of course.
SQL seems to be one of the most common examples of the use of a here-doc. I particular dislike using here-docs for SQL. By using join, I don't have to suffer due to SQL not allowing trailing commas, here-docs not supporting comments nor excluding lines conditionally, or lots of other things.
But most SQL I write ends up looking more like:
$db->select(
[ "this", "that", "theother", "COUNT(*) as things" ],
join( ' ',
"foo",
"LEFT JOIN bar USING( id )",
),
[
"? <= stamp",
"stamp < ?",
@exclude ? "this NOT IN ( ?? )" : (),
],
"GROUP BY this, that, theother",
"ORDER BY that, this",
"LIMIT $size OFFSET $skip",
"HAVING 1 < count(*)",
);
Which just boils down to this slightly longer code that I write when I don't have such trivial wrappers handy:
$db->prepare( join ' ',
"SELECT", join( ', ',
"this", "that", "theother", "COUNT(*) as things",
),
"FROM", join( ' ',
"foo",
"LEFT JOIN bar USING( id )",
),
"WHERE", join( ' AND ',
"? <= stamp",
"stamp < ?",
@exclude ? "this NOT IN ( ?? )" : (),
),
"GROUP BY this, that, theother",
"ORDER BY that, this",
"LIMIT $size OFFSET $skip",
"HAVING 1 < count(*)",
);
(Except that I like to build the SQL first rather than pass it directly to prepare(), so that I can easily get literal SQL in case I need to play with it interactively in a simple SQL tool. This includes replacing the placeholders with quote()d values.)
I find that scales much better as maintenance takes its toll.
I've also repeatedly suffered due to multi-line SQL dripping with whitespace, especially leading whitespace, tripping up perhaps somewhat naive DB tools I've needed to use to diagnose DB problems.
So I still feel quite justified in discouraging others from just running off and using here-docs, especially when I'm not planning on going on for thousands of words on how to avoid the many pitfalls.
|