Migration Details

Handling Deprecations From the Standalone Plugin Era

⏱ 11 min

The honest, useful version of this lesson is not a long list of renamed functions, because there mostly isn’t one. The standalone package’s own stated design goal was to “ship first as a Composer package, then migrate smoothly to core,” and that intent shows in the result: wp_register_ability(), wp_get_ability(), wp_get_abilities(), wp_unregister_ability(), and the category equivalents all carry the same names into core. What this lesson covers instead is the actual infrastructure around those functions that does change, and how to audit for it.

What you'll learn in this lesson
What genuinely stayed the same
The core function names and signatures your registration code already calls.
What actually changes: the scaffolding around it
Bootstrap requires, autoloader entries, and guard code that pointed at a package, not a function.
The full list of core functions to sanity-check against
So you can confirm your code isn't quietly calling something that only existed in the package.
Where to verify anything this lesson doesn't cover
The archived repo is still readable, and developer.wordpress.org is the current source of truth.
Prerequisites

Lesson 2’s Composer package removal. This lesson assumes that’s already done and you’re now auditing the code that’s left behind for anything subtler.

Step 1: Confirm the function surface you actually rely on

Core WordPress 6.9 ships these Abilities API functions and hooks. Grep your codebase against this list, anything you call that isn’t here is either misspelled or was a package-only helper that never made it into core as a public function:

Core Abilities API surface, WordPress 6.9+
CategoryFunctions / hooks
Registering abilitieswp_register_ability(), wp_unregister_ability()
Reading abilitieswp_has_ability(), wp_get_ability(), wp_get_abilities()
Registering categorieswp_register_ability_category(), wp_unregister_ability_category()
Reading categorieswp_has_ability_category(), wp_get_ability_category(), wp_get_ability_categories()
Registration hookswp_abilities_api_init, wp_abilities_api_categories_init
Execution hookswp_before_execute_ability, wp_after_execute_ability
grep -rn "wp_register_ability\|wp_get_abilit\|wp_has_ability\|wp_unregister_ability" --include="*.php" .

If everything your codebase calls is in that table, your actual registration logic needs zero changes, this is the reassuring part of the migration.

Step 2: Audit the scaffolding, not the function calls

The real differences show up around your registration code, not inside it:

Scaffolding to check for
Manual bootstrap requires
A require_once pointing into vendor/wordpress/abilities-api, covered in Lesson 2, but easy to have missed a second occurrence in an admin-only code path.
Composer autoloader assumptions
Any code assuming Composer's autoloader is present at all because the package needed it. Core functions need no autoloader.
Package-existence guards
A check like class_exists() against a class the package defined, rather than function_exists() against the actual WordPress function. Core's implementation is procedural, not class-based, at the public API surface.
Textdomain or translation loading tied to the package
If the old package shipped its own translations and your code loaded them, that loading call now does nothing useful and can be removed.

Step 3: Watch for anything package-specific this course hasn’t verified

The archived repository’s internal implementation details (its docs folder, its exact namespace usage for internal helper classes) aren’t fully documented in what’s publicly readable post-archival, and this course won’t state a specific renamed internal symbol as fact without being able to verify it directly. If your code reaches past the public wp_* functions into anything internal to the old package (a namespaced class, a helper the package exposed that wasn’t one of the functions listed in Step 1), treat that as a red flag regardless of whether this lesson names it specifically, and confirm its replacement against the current reference before migrating it.

// File: my-plugin.php

// A red flag: reaching for anything beyond the public wp_* function surface.
// use WP\AbilitiesApi\SomeInternalHelper;   // audit this specifically

// Safe: the same core functions the package always intended you to end up calling.
wp_register_ability( 'my-plugin/example', array( /* ... */ ) );
When in doubt, verify against the live reference

developer.wordpress.org/apis/abilities-api/ is the current, maintained source of truth for the public function surface. The archived GitHub repository is still readable and useful for historical context, but it’s frozen, it won’t reflect any clarification or correction WordPress core has made since archival.

Test it: run your existing test suite unchanged

If your plugin has PHPUnit coverage around ability registration, the most useful test here is simply running it, unmodified, against a core 6.9+ environment with the package removed:

composer remove wordpress/abilities-api
wp-env run tests-cli phpunit --filter=Abilities

Tests failing here point at real behavioral differences worth investigating individually, tests passing unchanged is the expected, common outcome given how closely core’s implementation follows the package’s original design.

Don't over-invest in defensive code you no longer need

Some migrated codebases accumulate elaborate compatibility shims, checking for both the package’s presence and core’s functions, running different code paths for each. Once you’ve committed to a WordPress 6.9+ minimum (Lesson 2), that dual-path complexity is pure maintenance burden with no remaining benefit, delete it rather than keep carrying it forward.

Recap

The core Abilities API’s function names and signatures deliberately match what the archived standalone package already provided, so your registration logic itself likely needs no rewrite. What actually needs auditing is the scaffolding around it: bootstrap requires, autoloader assumptions, and package-existence guards that should become function-existence or version checks instead. Anything reaching past the documented public function surface deserves individual verification against the current reference, not an assumption either way.

Resources & further reading

← The meta.mcp.public Requirement: Why Your Tools Aren't Showing Up Testing Compatibility Across WordPress Versions →