Back to DevLog

When Function Names Collide: A WordPress Plugin vs Theme Showdown

2 min read

Had one of those debugging sessions today that starts simple and ends with a completely locked-out WordPress admin. Fun times!

I was deploying the hb-dcc-import plugin to a client's cPanel site when PHP decided to throw a tantrum with fatal Cannot redeclare errors. Classic WordPress drama.

The Detective Work

Turns out I had 8 functions living double lives - existing in both my plugin (hb-dcc-import.php) and the theme's API file (hb-rest-api.php):

  • hb_sanitize_content
  • hb_ensure_page
  • hb_handle_page_status
  • hb_preview_status_impact
  • hb_count_referencing_pages
  • hb_cascade_remove
  • hb_cascade_reactivate
  • hb_execute_redirects

PHP was basically saying "I already know what hb_sanitize_content means, stop telling me again!" Fair point, PHP.

The Fix (That Became a Bigger Problem)

I went with function_exists() guards instead of renaming everything - because breaking existing callers seemed like a worse idea. Since plugins load before themes in WordPress, the plugin defines the functions first, then the theme politely skips re-defining them.

Got the plugin fixed and deployed. Then the theme file crashed on line 1588. Now I'm locked out of WP Admin entirely because even clicking "Appearance" triggers a fatal error popup. Can't get to Theme Editor, can't do anything.

The Escape Hatch

This is where WordPress recovery mode becomes your best friend. When everything goes sideways and you only have WP Admin access (no FTP, no SSH, no cPanel), that recovery email is literally the only way back in.

The plan is simple but requires patience:

  1. Wait for the "Your site is experiencing a technical issue" email
  2. Use the recovery link to get back in
  3. Deactivate the plugin temporarily
  4. Fix the theme file through Theme Editor
  5. Reactivate everything

Lessons Learned

Always think about load order when you have shared function names across plugins and themes. Those function_exists() guards are lifesavers, but you need them in BOTH places for this to work cleanly.

Also, having multiple deployment methods is clutch. When you're working with client sites where you only have WP Admin access, these kinds of lockouts can turn a 30-minute fix into a multi-step recovery process.

The fix is ready locally - just need to get it deployed. Sometimes the hardest part isn't writing the code, it's getting it where it needs to go.

Share this post