Skip to main content
Skip to main content

Developers Doc

Plugin Architecture & Lifecycle

9 mins read 7 Views 3+

Havenlytics is a real-estate plugin for WordPress built around a single, predictable entry point and a service-oriented core. This guide is written for professional WordPress developers who need to understand how the plugin loads, how its services are wired together, and where to hook in safely. If you are building an extension, an integration, or a child-theme override, understanding the architecture described here is the foundation for everything else in the Havenlytics Developer Documentation.

At the highest level, Havenlytics follows a strict layering: a procedural entry file (havenlytics.php) defines constants and loads shared code, a single final class HvnlyNab acts as the plugin singleton, and an orchestrator class HvnlyNab\Core\Bootstrap stands up every service. Nothing meaningful runs until WordPress fires plugins_loaded, and the plugin signals completion with a single action, hvnly_loaded.

The three architectural layers

Havenlytics deliberately separates concerns into three cooperating layers. Each layer has one job, and higher layers never reach around lower ones.

  • Entry file layer (havenlytics.php) — defines constants, loads migration dependencies and shared function files, wires the Composer autoloader, installs the global error handler, and defines the plugin singleton.
  • Singleton layer (HvnlyNab) — performs environment checks, registers activation/deactivation hooks, and defers real work to init_plugin() on plugins_loaded priority 5.
  • Service layer (HvnlyNab\Core\Bootstrap) — instantiates the property engine, shared services, settings system, cache and hook managers, and the admin/frontend split.

Note: The public accessor HVNLY_NAB() returns the HvnlyNab singleton. Prefer it over touching internal classes directly. See Service Container for the full accessor surface.

Namespace and autoloading

All PHP classes live under the HvnlyNab\ namespace, mapped via PSR-4 to the includes/ directory. The Composer autoloader is required early in the entry file, resolving __DIR__ . '/vendor/autoload.php' first and falling back to HVNLYNAB_PATH. Migration base types (MigrationTrait / MigrationInterface) are loaded via a dedicated bootstrap before the Composer autoloader so migration handlers can be discovered reliably.

<?php
// Access the plugin singleton anywhere after plugins_loaded (priority 5+).
$plugin = HVNLY_NAB();

echo $plugin->version();      // '3.3.1'
echo $plugin->plugin_path();  // absolute filesystem path (HVNLYNAB_PATH)

$helper = $plugin->get_helper();
$db     = $plugin->database();
$engine = $plugin->engine();

Request lifecycle

Every request that loads WordPress with Havenlytics active follows the same deterministic path. The plugin does almost nothing at file-include time beyond defining constants and loading shared code — the real boot happens on plugins_loaded.

  1. Plugin load (file include). havenlytics.php defines constants, loads the migration bootstrap and shared function files, requires the Composer autoloader, initializes ErrorHandler, runs ConflictChecker, and defines the HvnlyNab class. The final line calls HVNLY_NAB(), instantiating the singleton.
  2. Constructor. HvnlyNab::__construct() runs basic_environment_check() and EnvironmentChecker::check(), registers lifecycle hooks, then schedules init_plugin() on plugins_loaded priority 5.
  3. plugins_loaded (priority 5) — init_plugin(). Bails if a fatal was recorded, initializes telemetry (silent-failing), creates and initializes Bootstrap, wires the Elementor integration on plugins_loaded priority 20, then fires do_action('hvnly_loaded'). The whole body is wrapped in try/catch that records fatals.
  4. Bootstrap::init(). Runs the ordered service startup: engine, shared services, settings system, version check, service split, hook registration, event scheduling, and enterprise features. See Bootstrap Process for the full step-by-step.
  5. HookManager::register_hooks(). Registers admin menu redirects, plugin action links, rewrite rules, cache AJAX handlers, cache invalidation, localization, and scheduled cleanup.
  6. do_action(‘hvnly_loaded’). The public signal that Havenlytics is fully initialized. This is the correct place to attach extensions that depend on the plugin being ready.
<?php
/**
 * Run extension code only after Havenlytics has fully booted.
 * hvnly_loaded fires at the end of init_plugin() on plugins_loaded priority 5.
 */
add_action( 'hvnly_loaded', function () {
    if ( ! function_exists( 'HVNLY_NAB' ) ) {
        return;
    }

    $engine = HVNLY_NAB()->engine();
    // Safe to call engine, helper, and database accessors here.
} );

Tip: Do not hook your integration on plugins_loaded at a priority earlier than 5 and expect Havenlytics services to exist. Use the hvnly_loaded action instead — it is the contract that guarantees the container is populated.

Environment gates

Havenlytics enforces minimum platform requirements twice, defensively. The constructor first calls basic_environment_check() (PHP >= 7.4, WordPress >= 6.0). If that fails, the plugin queues admin notices and self-deactivates on admin_init, and the constructor aborts. A second gate, EnvironmentChecker::check(), repeats the PHP/WP check and aborts on failure. Both thresholds are hardcoded.

RequirementMinimumEnforced by
PHP7.4basic_environment_check() + EnvironmentChecker::check()
WordPress6.0basic_environment_check() + EnvironmentChecker::check()

Warning: Because the environment gate can abort the constructor, extensions must never assume HVNLY_NAB() returns a fully initialized instance on unsupported platforms. Always guard with function_exists('HVNLY_NAB') and hook on hvnly_loaded.

Activation, deactivation, and uninstall

Lifecycle hooks are registered in the constructor via register_lifecycle_hooks().

Activation

The register_activation_hook closure is wrapped in try/catch and records fatals on failure. It calls Installer::activate(), re-arms the onboarding redirect (deletes hvnly_activation_redirect_done, sets hvnly_activation_redirect = 1 plus a transient), and runs RewriteRulesManager::activation_setup()Installer::activate() is guarded for existing sites: if a stored DB version is present it runs migrations only, otherwise it runs a full install().

Deactivation

register_deactivation_hook calls HvnlyNab::deactivate(), which clears scheduled events via Scheduler::clear_scheduled_events(), deletes the redirect options and transient, deletes hvnly_flush_rewrite_once, and flushes rewrite rules.

Uninstall

register_uninstall_hook is commented out. Cleanup runs from uninstall.php instead, guarded by WP_UNINSTALL_PLUGIN. It deletes a curated list of options (cache options, demo-import flags, builder sections, hvnly_db_version, migration history, settings, install flags, page-id options), removes options and transients matching hvnly_% / _hvnly_% patterns and migration backups, deletes post/term/user meta with keys like _hvnly_% or _hvnlynab_%, unschedules the four cron events, and flushes the object cache.

Security: uninstall.php only runs data destruction when WP_UNINSTALL_PLUGIN is defined by WordPress. Never invoke it directly, and never reproduce its bulk DELETE ... LIKE 'hvnly_%' queries in your own code — they are intentionally broad and destructive.

Migrations run on activation and page-load

Data migrations are not activation-only. They run in three situations: on activation (Installer::install / activate call Migrator::run(true)); on page-load (Bootstrap::check_version_update on every init, when a migration is needed or the stored DB version is behind HVNLYNAB_VERSION); and on manual retry via the admin-post action hvnly_retry_migrations. This means an upgraded plugin folder self-heals its data the first time an admin loads a page — a full re-activation is not required.

Note: Page-load migrations only execute in eligible contexts (WP-CLI, or admin requests that are not AJAX, or when HVNLY_FORCE_MIGRATIONS is set). This keeps front-end requests fast while still guaranteeing data consistency for admins.

Boot sequence at a glance

  1. Define constants (HVNLYNAB_VERSION, paths, URLs).
  2. Load migration bootstrap (MigrationTrait / MigrationInterface).
  3. Load shared function files (utility, email, field options, deletion safety, template bootstrap).
  4. Require Composer autoloader.
  5. Re-run migration dependency loading after autoload.
  6. Initialize ErrorHandler (admin notice + return if the class is missing).
  7. Instantiate ConflictChecker.
  8. Register ContactAgentAjaxRegistrar on plugins_loaded priority 4.
  9. Define HvnlyNab and call HVNLY_NAB() to build the singleton.
  10. Constructor: environment checks, lifecycle hooks, schedule init_plugin() at priority 5.
  11. init_plugin(): telemetry, Bootstrap::init(), Elementor wiring at priority 20, do_action('hvnly_loaded').

Core constants reference

ConstantValue / meaning
HVNLYNAB_VERSION3.3.1 — current plugin version.
HVNLYNAB_FILEAbsolute path to havenlytics.php (__FILE__).
HVNLYNAB_BASENAMEPlugin basename (plugin_basename).
HVNLYNAB_SLUGhavenlytics — plugin slug and text domain.
HVNLYNAB_PATHAbsolute filesystem path to the plugin directory.
HVNLYNAB_URLURL to the plugin directory.
HVNLYNAB_ASSETS_PATH / HVNLYNAB_ASSETS_URLPath and URL to bundled assets.
HVNLYNAB_BUILD_PATH / HVNLYNAB_BUILD_URLPath and URL to compiled build output.
HVNLYNAB_INCLUDESPath to the includes/ directory (PSR-4 root).
HVNLYNAB_LANG_DIRPath to translation files.
HVNLYNAB_TEMPLATE_DEBUGfalse by default; toggles template resolution debugging.
HVNLY_FORCE_MIGRATIONSRuntime-checked; forces migrations to run when defined true.
<?php
// Build a URL to a bundled asset using the plugin's own accessor.
$css_url = HVNLY_NAB()->assets_url( 'css/frontend.css' );

// Or use the constant directly for build output.
$script  = HVNLYNAB_BUILD_URL . 'workspace/workspace.js';

Best practices

  • Always access the plugin through HVNLY_NAB() and its accessors, never by newing up core classes.
  • Attach extension logic on hvnly_loaded, not on early plugins_loaded priorities.
  • Use the defined path/URL constants instead of hardcoding directory locations.
  • Guard every integration with function_exists('HVNLY_NAB') so your code degrades cleanly if Havenlytics is deactivated or blocked by an environment gate.

Performance recommendations

  • Because the file-include phase only defines constants and loads shared functions, keep your own plugin’s include phase equally light — defer heavy work to hvnly_loaded.
  • Page-load migrations are gated to admin, non-AJAX requests, so they do not tax front-end traffic. Avoid forcing HVNLY_FORCE_MIGRATIONS in production request paths.

Common mistakes

  • Assuming activation runs migrations only. Migrations also run on admin page-load; test upgrades by loading an admin screen, not just re-activating.
  • Expecting an uninstall hook. register_uninstall_hook is commented out; cleanup lives in uninstall.php.
  • Reaching into private services. Services without a dedicated accessor live on the private container and are effectively internal.
  • Ignoring the environment gate. On PHP < 7.4 or WP < 6.0 the constructor aborts and self-deactivates.

Need more help?

Can't find what you're looking for? Our team and community are here to help you ship faster.