Documentation · Technical Documentation
Plugin Bootstrap And Lifecycle
This document describes how the ADP Car Market Hub plugin loads, what happens during activation, deactivation and uninstall, and which migrations the plugin runs as it boots.
When to use this document
Read this document if you need to:
- Trace the order in which plugin code executes during a WordPress request.
- Understand which actions run on activation and deactivation (capabilities, default pages, custom tables, scheduled events).
- Plan an upgrade or troubleshoot a migration that did not apply.
- Decide where to hook custom code so that it runs after the plugin's services are available.
Overview
The plugin is bootstrapped from the main file
adp-car-market-hub.php. That file is short by design and only does
work that must happen at file-load time. All feature code is loaded
through a PSR-4-style autoloader and wired up by the
AS24CI\Plugin singleton on the plugins_loaded action.
Constants defined at load time
adp-car-market-hub.php defines the following constants before any
hooks run:
AS24CI_VERSION— Current plugin version (kept in sync with the plugin header andreadme.txt).AS24CI_PLUGIN_FILE— Absolute path to the main plugin file.AS24CI_PLUGIN_DIR— Directory of the plugin (with trailing slash).AS24CI_PLUGIN_URL— URL to the plugin directory, useful for asset enqueuing.
The file also calls require_once AS24CI_PLUGIN_DIR . 'src/Core/Helpers.php'
to load shared procedural helpers and conditionally defines the global
helpers as24ci_format() and as24ci_get_available_ai_models() if no
function with the same name has been declared earlier.
Autoloader
Immediately after the constants, the file registers a small
PSR-4-compatible autoloader for the AS24CI\ namespace. Class names
are mapped to file names of the form
class-as24ci-<lower-dashed-name>.php. The autoloader looks first in
includes/ and falls back to includes/admin/ for admin-only classes.
Hook registration at load time
The main file registers the following hooks before any feature code runs:
| Hook | Callback | Purpose |
|---|---|---|
plugins_loaded (priority 1) | as24ci_load_textdomain | Loads the adp-car-market-hub text domain from /languages. |
plugins_loaded (default priority) | AS24CI\Plugin::init | Constructs the singleton and registers all feature hooks. |
register_activation_hook | AS24CI\Plugin::activate | Runs once when the plugin is activated. |
register_deactivation_hook | AS24CI\Plugin::deactivate | Runs once when the plugin is deactivated. |
cron_schedules | as24ci_add_custom_cron_intervals | Registers the custom WP-Cron interval as24ci_every_5_minutes (300 s). |
plugin_action_links_<basename> | as24ci_add_plugin_action_links | Adds "License activation" and "Settings" links on the WordPress Plugins screen. |
plugin_row_meta | as24ci_add_plugin_row_meta | Adds a "Documentation" link to the plugin's row meta. |
The main file also bootstraps the Content Studio module at load time,
independently of the AS24CI\Plugin router:
register_activation_hook→AS24CI\Content_Studio_Repository::maybe_create_tables(creates the Content Studio tables on activation).admin_menu(priority 30) →AS24CI\Admin_Tab_Content_Studio::register_menu.plugins_loaded(priority 20) →AS24CI\Content_Studio_Admin_Worker::register_hooks.
The custom WP-Cron interval is registered unconditionally so that WordPress always knows about it, even when individual feature toggles that use it are disabled.
Bootstrap sequence (normal request)
When WordPress loads the plugin during a normal request, the following steps happen in order:
- The main file is included; constants, autoloader and the hooks listed above are registered.
- WordPress fires
plugins_loadedat priority 1:as24ci_load_textdomain()runsload_plugin_textdomain()so that translations are available before any translatable string is rendered. - WordPress fires
plugins_loadedat the default priority:AS24CI\Plugin::init()constructs the singleton. - The constructor of
AS24CI\Plugin: - Instantiates the shared services:Logger,Client,Image_Importer,Vehicle_Repository,Importer,Scheduler. - CallsVehicle_Field_Resolver::set_repository()so the central field resolver can read from the vehicles table. - CallsVehicle_Deleter::set_repository()andVehicle_Deleter::register_hooks()so every permanent-delete path (native WordPress, importer full sync, bulk action) goes through a single, idempotent cleanup. - Callsregister_hooks()to wire all features for the current request. register_hooks()wires twoadmin_initcallbacks (they are wired duringplugins_loadedbut only fire on a lateradmin_initinwp-admin): -AS24CI\Plugin::maybe_upgrade_caps()— ensures roles and capabilities exist for the current plugin version. -AS24CI\Plugin::maybe_upgrade()— runs option/data migrations.register_hooks()then registers each subsystem's own hooks: - Always-on:CPT,Taxonomies,Leads_CPT,Contact_Form,Templates,Assets,Ajax,Archive_Filters,Cron_Endpoint,License_Manager,License_Refresh_Signal,Webhooks,Analytics,Ai_Assistant,Data_Quality_Scanner,Pricing_Engine(with daily schedule),Financing_Calculator,Rest_Api,Locations,Seller_Profile_Fields, plus the scheduler. The privateUpdateris also wired here, gated to admin / WP-Cron / WP-CLI contexts. - Gated by feature options onAS24CI\Options:Schema(plusSeo_Compatibility),Sitemap,Social_Share,Pdf_Datasheet,Favorites,Compare,Export,Bulk_Actions,Search_Agent. - Admin only (is_admin()true): theAS24CI\Admincontroller,Admin_Team(CMH Team),Update_VisibilityandAdmin_Setup_Wizard, and — whenOptions::FEATURE_DASHBOARD_WIDGETis enabled — the dashboard widget.- WordPress proceeds with the rest of the request lifecycle. Most
feature classes register additional hooks on later actions such as
init(post types, taxonomies, table creation for Search Agents),rest_api_init(REST routes),wp_footer(analytics tracking pixel) oradmin_init(admin-only table upgrades).
Activation
AS24CI\Plugin::activate() is registered with
register_activation_hook and runs once when an administrator
activates the plugin. It performs the following work, in order:
- Calls
seed_safe_defaults(), which usesadd_option()to install conservative defaults for first setup. Existing values are never overwritten. Notable defaults: -as24ci_default_currency=EUR. -as24ci_default_post_status=draftso admins can review mappings before publishing. -as24ci_max_images=30(a finite cap rather than unlimited). -as24ci_full_sync=0(hard delete remains opt-in). - Frontend-friendly features (sitemap, schema, favorites, compare, financing, dashboard widget, export, lazy loading, layout manager) default on; tracking, external, batch and risky features (analytics, REST API, AI Assistant, social share, PDF datasheet, test drive) default off. - Calls
add_option( Options::SETUP_FIRST_ACTIVATION_AT, gmdate('c') )to record the first-activation timestamp used by the Setup Wizard admin notice. Becauseadd_option()is a no-op when the value exists, reactivations preserve the original date. - Calls
ensure_roles_and_caps()which: - Creates theas24ci_editorrole if it does not exist. - Grants theas24ci_car/as24ci_carscapability set to bothas24ci_editorandadministrator. - Adds themanage_as24_importscapability (thePlugin::CAP_MANAGEconstant) to administrators only. This capability gates the importer, settings, tools and logs UI. - Sets
as24ci_caps_versionto1so the capability migration does not run again on subsequent requests. - Sets
as24ci_db_versionto5so the data migrations defined inmaybe_upgrade()are skipped on a fresh install. - Calls
maybe_create_default_pages()to optionally create theCars,Compare CarsandFavoritespages and store their post IDs inas24ci_page_archive_id,as24ci_page_compare_idandas24ci_page_favorites_id. The behaviour is gated by theas24ci_create_default_pagesoption and theas24ci_default_pages_enabledandas24ci_default_pagesfilters. - Calls
Analytics::maybe_create_table()to create the analytics table. - Calls
Vehicle_Repository::maybe_create_table()to create the dedicated vehicles table. - Schedules the daily analytics retention cleanup
(
as24ci_daily_cleanup) if not already scheduled. - Calls
License_Manager::ensure_cron_scheduled()to schedule the daily license re-validation event (as24ci_license_refresh). - Calls
CPT::register_post_type()and thenflush_rewrite_rules()so that the/carsarchive works immediately after activation. - Logs
Plugin activated (v<version>).viaAS24CI\Logger.
The Content Studio tables are created by a separate activation hook
(Content_Studio_Repository::maybe_create_tables) registered in the
main plugin file, not by Plugin::activate().
activate() deliberately does not seed default API URLs. Missing
configuration is surfaced through the system status / health UI and
through client errors when the importer runs.
Capability and data migrations
Two static methods are wired on admin_init (not plugins_loaded)
to keep existing installations in sync with the current plugin
version. Hooking on admin_init avoids forcing the pluggable
authentication stack to load early on every request:
maybe_upgrade_caps()— Wired onadmin_init. It guards against AJAX, cron and REST requests (returning early in those contexts) and uses a per-request static guard so the migration body runs at most once. It is not gated onmanage_options. Comparesas24ci_caps_versionagainst the target value (1at the time of writing) and re-runsensure_roles_and_caps()if needed.maybe_upgrade()— Wired onadmin_init; returns early unlessis_admin(). Comparesas24ci_db_versionagainst the target value (5) and applies any outstanding migration steps. Each step is idempotent:- Migration 1: marker only, no data changes.
- Migration 2: forces the analytics-consent option
(
Options::ANALYTICS_REQUIRE_CONSENT) to0. Administrators can re-enable it from the settings UI. - Migration 3: migrates the legacy
FINANCING_PLACEMENToption to the new Layout Manager zones. The migration only writes to a Layout Manager zone if that zone has not been saved yet, so existing layouts are preserved. - Migration 4: re-applies
seed_safe_defaults()so existing installs receive any new safe defaults without overwriting values the administrator has already set. - Migration 5: protects plaintext secrets at rest. Reversible
secrets (
Options::CLIENT_SECRET,Options::HUB_API_KEY,Options::WEBHOOK_SECRET) are wrapped viaSecrets::encrypt(), and the cron token (Options::CRON_TOKEN) is replaced with its keyed HMAC hash viaSecrets::hash_token(). The migration is idempotent (already-protected rows are skipped) and the cached OAuth bearer token is cleared so it is re-stored through the encrypted path. After all applicable steps, the option is updated to the current target version.
Deactivation
AS24CI\Plugin::deactivate() runs once when an administrator
deactivates the plugin. Existing data is preserved. The method:
- Calls
wp_clear_scheduled_hook( Scheduler::CRON_HOOK )to remove pendingas24ci_scheduled_importevents. - Calls
wp_clear_scheduled_hook( Analytics::ANALYTICS_CLEANUP_CRON_HOOK )to remove the daily analytics retention event. - Calls
Ai_Assistant::clear_ai_queue_schedule()to remove the background AI queue cron event. - Calls
Pricing_Engine::clear_schedule()to remove the daily pricing-engine cron event. - Calls
License_Manager::clear_cron()to remove the daily license re-validation event (as24ci_license_refresh). - Calls
wp_clear_scheduled_hook( 'as24ci_competitor_watcher_cron' )as legacy cleanup only — the Competitor Watcher feature (and its class) has been removed; the hook name is hard-coded here so any leftover scheduled event on installations upgraded from older versions is still cleared. - Deletes the
Scheduler::LOCK_TRANSIENTtransient (as24ci_cron_import_running) in case the plugin is deactivated while an import is in progress. - Calls
flush_rewrite_rules()and logsPlugin deactivated.
Because deactivation does not touch the database tables, custom post types or options, reactivating the plugin restores the previous state.
Uninstall
When the plugin is deleted from the WordPress admin (not just
deactivated), WordPress executes uninstall.php. The script:
- Loads
AS24CI\Options(if the file exists) so the authoritative list of option keys can be derived fromOptions::get_all_keys(), plus a small set of additional keys (as24ci_models_cache_keys,as24ci_page_archive_id,as24ci_page_compare_idand the legacyas24ci_api_total_cache). A hard-coded fallback list is used if the class cannot be loaded. - Deletes plugin transients
(
as24ci_access_token,as24ci_cron_import_running,as24ci_image_queue_running). - Unschedules pending events for
as24ci_scheduled_importandas24ci_daily_cleanup. - Reads the
as24ci_delete_data_on_uninstalloption. When the value is1, the script deletes: - Allas24ci_carposts (forced). - All attachment IDs stored in each car's_as24ci_image_idspostmeta. Manual gallery attachments (_as24ci_manual_image_ids) are intentionally not deleted. - The featured image attachment of each car (defensive cleanup). - Allas24ci_leadposts. - The activation-created Cars, Compare and Favorites pages. - Always drops the custom tables (regardless of the opt-in flag):
-
{$wpdb->prefix}as24ci_analytics-{$wpdb->prefix}as24_vehicles-{$wpdb->prefix}as24ci_search_agents-{$wpdb->prefix}as24ci_content_studio_assets-{$wpdb->prefix}as24ci_content_studio_jobsThe matching schema-version options (as24ci_vehicles_db_version,as24ci_search_agent_db_version,as24ci_content_studio_db_version) are removed as well. - Deletes all collected option keys for the site.
- On multisite installations the same routine runs once per site via
switch_to_blog().
Configuration reference
The bootstrap and lifecycle behaviour is influenced by the following
options (all defined as constants on AS24CI\Options). The full
catalogue is documented in the Options And Settings Storage
document; only the lifecycle-relevant entries are listed here.
| Option key | Constant | Used by |
|---|---|---|
as24ci_caps_version | Options::CAPS_VERSION | maybe_upgrade_caps() to decide whether caps must run. |
as24ci_db_version | Options::DB_VERSION | maybe_upgrade() to decide which migrations to apply. |
as24ci_create_default_pages | Options::CREATE_DEFAULT_PAGES | maybe_create_default_pages() during activation. |
as24ci_page_archive_id | n/a | Stores the ID of the activation-created Cars page. |
as24ci_page_compare_id | n/a | Stores the ID of the activation-created Compare page. |
as24ci_page_favorites_id | n/a | Stores the ID of the activation-created Favorites page. |
as24ci_delete_data_on_uninstall | Options::DELETE_DATA_ON_UNINSTALL | uninstall.php to decide whether to delete content. |
Operational notes
register_activation_hookis fired only when an administrator activates the plugin from the Plugins screen. Updating the plugin files does not trigger activation; that is whymaybe_upgrade()exists.- Because
maybe_upgrade()andmaybe_upgrade_caps()are hooked onadmin_initand only run during real admin page loads (not AJAX, REST or cron), frontend-only requests do not pay the cost of migration checks. The first admin page load after an update applies any outstanding migration. - The plugin's WP-Cron lock TTLs are conservative: the import lock
(
Scheduler::LOCK_TTL) is approximately 40 minutes. If a process is killed mid-run, the lock is released automatically when the transient expires. Manual recovery is described in the scheduler documentation. - Uninstall always drops the analytics, vehicles and search-agent
tables because they may contain personal or visitor data. Posts and
options are only removed when the administrator has opted in via the
as24ci_delete_data_on_uninstallsetting.
Troubleshooting
- Activation does not create the Cars page. Confirm that
as24ci_create_default_pagesis set to1and that no theme or other plugin is filteringas24ci_default_pages_enabledtofalse. The page is also not recreated if a page with the same title already exists; in that case its ID is reused. - Custom capabilities are missing for an administrator. Visit any
admin page so
maybe_upgrade_caps()can run onadmin_init, or temporarily deleteas24ci_caps_versionfromwp_optionsto force the migration to re-execute. The migration is skipped on AJAX, REST and cron requests, so it must run during a real admin page load. - A migration step did not apply. Check the value of
as24ci_db_version. If it already equals the target, the migration has already run. Lower the value manually only if you understand the consequences for your installation. - The
/carsarchive returns a 404 immediately after activation. Activation already callsflush_rewrite_rules(). If the archive is still not reachable, visit Settings → Permalinks and save once to force WordPress to rebuild its rewrite cache.