Point 5 is the detail that actually determines whether this refactor ships without incident: adding a new auto-firing hook without accounting for existing direct calls is how a “harmless” modernization ends up rendering the banner twice on the very sites you were trying not to disrupt.
Code
Refactor legacy plugin code that echoes HTML directly into using hooks properly
A scenario for turning a function that echoes a banner straight into theme files into a proper hook-driven template tag, without breaking sites that call the old function directly.
Works with Claude / GPT700 uses★ 4.1
The prompt
code-refactor-legacy-echo-html-to-hooks
This legacy plugin function is called directly from `header.php` in several client
themes:
```php
function myplugin_show_banner() {
global $post;
echo '<div class="promo-banner">';
echo '<h2>' . get_option('myplugin_banner_text') . '</h2>';
echo '<a href="' . get_option('myplugin_banner_link') . '">Shop Now</a>';
echo '</div>';
}
```
CONTEXT: [I want to modernize this into hook-based output so it's themeable and
extensible, but at least six client sites currently call `myplugin_show_banner()`
directly in their theme templates, so it can't just disappear]
Refactor this like a senior dev handling a real backward-compatibility constraint,
in order:
1. Identify the concrete problems with the current version beyond style: no output
escaping (`get_option()` values go straight into HTML, so a banner text field with
an unescaped `<` or a compromised option value becomes a stored XSS vector), and no
way for a theme or another plugin to customize or suppress the banner without
editing this function directly.
2. Refactor the output to fire on a hook (`add_action('myplugin_before_content',
'myplugin_render_banner')`) with proper escaping (`esc_html()` for the text,
`esc_url()` for the link), and make the banner's visibility and content filterable
via `apply_filters('myplugin_banner_args', $args)` so other code can adjust it
without a copy-paste override.
3. Preserve backward compatibility explicitly: keep `myplugin_show_banner()` as a thin
wrapper that calls the new render function directly (rather than removing it), so
the six client themes calling it directly keep working unchanged, while new
integrations use the hook.
4. Add a `_doing_it_wrong()` or inline code comment marking the old function as
soft-deprecated, pointing future developers (including future you) to the hook-based
approach, without actually breaking anything for existing callers.
5. Flag the edge case: since the new hook approach means the banner can now fire
automatically on `myplugin_before_content` in themes that never explicitly called
`myplugin_show_banner()`, decide and document whether the hook should be added to
the theme's `header.php` by the theme, or auto-injected via `wp_head`/`the_content`
filter for themes with no existing integration, so it doesn't double-render on the
six sites that already call the old function AND also have the new hook fire.
Replace the function, hook name, and legacy call sites with your actual code.