A client came to me with a “quick job” the other day. They run a busy blog with an engaged comment section, and they wanted to highlight reader comments inside their posts with a simple shortcode. The feature itself was easy. The problem was the code I inherited: the previous dev had mashed all the HTML generation right inside the shortcode function, one big string of concatenated divs and spans.
That is a reliable way to create a maintenance headache. The moment you need a small tweak, say “can we add an avatar?”, you have to dig through PHP logic just to change a snippet of markup. It’s brittle, and it’s how you end up with spaghetti code. The fix is to handle the HTML generation properly instead.
My first thought, I’ll admit, was to clean up the existing function, maybe use a sprintf to make it more readable. Then the change requests started piling up. “Can we have a different layout for comments by the post author?” “Can we add a ‘Verified Reader’ badge?” My simple function was about to explode into a dozen if statements. That is the point where you back up and decouple the logic from the presentation.
The right way to generate HTML in WordPress
Instead of cramming everything into one function, create a dedicated class whose only job is to generate the HTML. It’s a cleaner approach, and it builds on an idea I saw over at carlalexander.ca: separate the “what” (the data, such as the comment object) from the “how” (the HTML output). The HTML itself lives in a separate template file.
This makes life easier for everyone. The PHP dev works on the class, and a front-end dev can change the template file without touching the logic.
class MyPlugin_CommentGenerator
{
private $template_path;
public function __construct($template_name = 'highlighted-comment.php')
{
// Allow themes to override the template
$located = locate_template('myplugin/' . $template_name);
if (!empty($located)) {
$this->template_path = $located;
} else {
$this->template_path = __DIR__ . '/templates/' . $template_name;
}
}
public function generate(WP_Comment $comment)
{
if (!is_readable($this->template_path)) {
return '<!-- Template not found -->';
}
ob_start();
// Make the $comment object available to the template file
include $this->template_path;
return ob_get_clean();
}
}
// And the shortcode function becomes clean and simple:
function myplugin_display_comment_shortcode($atts) {
$a = shortcode_atts(['id' => 0], $atts);
$comment_id = (int)$a['id'];
if (empty($comment_id)) {
return '<!-- Missing comment ID -->';
}
$comment = get_comment($comment_id);
if (!$comment instanceof WP_Comment) {
return '<!-- Invalid comment -->';
}
$generator = new MyPlugin_CommentGenerator();
return $generator->generate($comment);
}The shortcode function is clean now. All it does is handle the data: get the comment ID and fetch the comment object. It then hands the rendering off to the MyPlugin_CommentGenerator class, with no HTML in it at all.
Why separate logic from HTML
The point is to write code that you, or another developer, can maintain six months from now. Separating PHP logic from HTML templates keeps small changes small. This isn’t only for shortcodes. It applies to:
- Custom widget outputs
- AJAX/REST API responses that return HTML fragments
- Complex meta box displays
Thinking in terms of templates and data models pushes you toward more organized code.
This stuff gets complicated fast. If you’re tired of debugging someone else’s mess and just want your site to work, drop my team a line. We’ve probably seen it before.