Plugin System
Composer-managed plugins, yax mu-plugin architecture, custom WHA plugins, and the update workflow.
Plugin Management Strategy
Plugins come from four sources, each managed differently:
| Source | Location | Managed by | Examples |
|---|---|---|---|
| Composer (wpackagist) | src/wp-content/plugins/ | composer.json | Yoast, Redirection, User Switching |
| Composer (private) | src/wp-content/mu-plugins/ | composer.json | All yax-* mu-plugins |
| Manual (proprietary) | src/wp-content/plugins/ | Committed to git | ACF Pro, Schema Pro, BrightEdge |
| Custom (WHA) | src/wp-content/mu-plugins/ | Committed to git | All wha-* mu-plugins |
composer.json is the source of truth for WordPress core and plugin versions. The private Composer repository at composer.joeyyax.app hosts the yax-* packages.
Composer-Managed Plugins
Third-party plugins are installed via wpackagist and pinned in composer.lock. These cover SEO (Yoast), redirects, content management utilities, form protection (Cloudflare Turnstile), and dev tools (Query Monitor, Debug Bar). See composer.json under the wpackagist-plugin/* entries for the current list.
Dev-only plugins like debug-bar and query-monitor are blocked in production via the WORDPRESS_BLOCK_PLUGINS environment variable.
Yax MU-Plugin System
The yax-* mu-plugins are a shared framework used across WHA and other projects. They're installed via Composer from the private repository and auto-discovered by the loader.
How the Loader Works
000yax-loader.phpis the bootstrap file (auto-generated by the entrypoint script, gitignored)- It loads
yax-loader/yax-loader.phpwhich scansmu-plugins/foryax-*directories - Each plugin declares a
Priorityheader — lower numbers load first - Dependencies declared via
Requires Pluginsheader are auto-validated - Append
.disabledto a directory name to skip it
Service Lifecycle
Yax plugins follow a three-phase lifecycle:
- boot() — Load dependencies, initialize early features (runs on
muplugins_loaded) - register() — Register with WordPress, configure hooks (runs on
yax/register) - freeze() — Lock configuration, prevent changes (runs on
plugins_loaded)
Core Yax Plugins
| Plugin | Priority | Purpose |
|---|---|---|
yax-common | 0 | Service container ($yax), shared utilities (text, CSS, HTML, data formatting, heading manager, nav menu extensions, asset versioning, SVG support) |
yax-model | 1 | Code-first model system — fluent builder API for register_post_type(), register_taxonomy(), metaboxes, options pages |
yax-query | 2 | Fluent WP_Query builder — yax_query(), yax_posts(), yax_terms(), yax_page(), yax_post() |
yax-components | 2 | Component rendering — get_component(), set_props(), get_components(), pluggable field providers (ACF, Null, custom) |
yax-loader | — | Auto-discovery and priority-based loading of all yax-* plugins |
Other Yax Plugins
The remaining yax-* plugins handle infrastructure concerns: media offloading (yax-offload-media), Composer version sync (yax-version-sync), security hardening (yax-security), performance (yax-performance), search (yax-meilisearch), analytics, content sync, cleanup, and more. See composer.json (project root) under the joeyyax/* entries for the current list.
Custom WHA Plugins
The wha-* mu-plugins are committed directly to the repo (not Composer-managed). Most register a custom post type with its associated taxonomies and business logic. A few handle integrations and specialized functionality. See src/wp-content/mu-plugins/wha-* for the full list and Custom Post Types for the data model.
Doctor.com Sync (wha-doctorcom-sync)
Syncs provider and location data from Doctor.com's API into WordPress. This is how provider profile data (bios, photos, specialties, locations) stays in sync with the Doctor.com directory.
- Architecture: API client, FieldMap (maps Doctor.com fields to WordPress/ACF fields), ProviderSync, LocationSync, Scheduler
- Admin UI: Settings page for API credentials and sync configuration, plus provider and location management pages for manual sync
- Sync modes: Individual provider/location sync via AJAX, or batch sync via WP-Cron
- Cron schedules: Configurable auto-sync intervals for both providers and locations (
wha_sync_providers,wha_sync_locations) - Audit logging: All sync operations are logged via
yax-audit-log(synced, failed, batch start/complete, paused, resumed) - Dependencies:
yax-common,yax-model,yax-audit-log
Provider Ratings (wha-provider-ratings)
Fetches and stores PressGaney patient satisfaction ratings. This is a data-only plugin — it queries the ratings API, stores the results, and exposes getters. The theme handles display.
- Data stored: Aggregate rating (overall score + response count), individual question scores, and patient comments per provider
- Display control: Configurable thresholds determine whether ratings show for a given provider. Providers below the threshold don't display ratings at all.
- Theme functions:
wha_get_ratings($id),wha_get_ratings_questions($id),wha_get_ratings_comments($id),wha_should_display_ratings($id) - Sync: Scheduled via WP-Cron, manual sync available in wp-admin
- Components: The
stars.phpUI component andprovider-ratingssection component consume this data - Dependencies:
yax-common,yax-model,yax-audit-log
Appointments (wha-appointments)
Appointment scheduling wizard with REST API, custom rewrite rules, and MyHealth patient portal integration.
- MyHealth integration: Links appointment types to the MyHealth patient portal for online scheduling
- Query helpers: Custom queries for appointment type lookups
- ACF fields: Registered via
yax-modelfor appointment type configuration
Protected Content (wha-protected-content)
Page-level, component-level, and CTA-level authentication with role-based access control.
- Granular visibility: Content can be restricted at the page, section component, or individual CTA level — not just entire pages
- Role management: Supports role self-assignment, allowing users to select their role (e.g., patient vs. provider) which controls what content they see
- Theme integration:
yax-componentschecks visibility before rendering each section, so protected sections are invisible to unauthorized users without any theme code changes
Non-Composer Plugins
These proprietary plugins are committed to git and updated manually:
| Plugin | Notes |
|---|---|
| ACF Pro | Advanced Custom Fields Pro — core dependency for all flexible content. Not available on wpackagist. |
| Schema Pro | Structured data / schema.org markup |
| BrightEdge | SEO platform integration |
| SendGrid | Email delivery |
These are plugins that aren't available through Composer (proprietary, paid, or not on wpackagist). They're committed directly to the repo and updated manually: download the new version, replace the plugin folder, commit.
Other Committed Files
| File | Purpose |
|---|---|
healthz.php | Health check endpoint at /healthz for uptime monitoring |
redirects.php | Custom redirect rules (mu-plugin) |
carbon-fields-stub.php | Stub for legacy theme compatibility |
yax-loader.wha.php | WHA-specific loader customizations |
Updating Plugins
Composer-Managed (wpackagist)
Two paths:
From wp-admin (preferred):
- Click "Update" in the WordPress admin plugins page
yax-version-syncautomatically runscomposer require wpackagist-plugin/plugin-name:X.Y.Z- Commit the updated
composer.jsonandcomposer.lock
From CLI:
composer require wpackagist-plugin/plugin-name:^X.Y
git add composer.json composer.lock
git commit -m "Update plugin-name to X.Y"WordPress Core
Same as plugins — click "Update" in wp-admin, yax-version-sync handles the Composer manifest update:
# Or manually:
composer require johnpbloch/wordpress-core:X.Y.ZAdding a New Plugin
If available on wpackagist (most free plugins):
composer require wpackagist-plugin/new-pluginFrom wp-admin: Install the plugin normally. An admin notice with "Add to composer manifest" button appears — click it and commit.
If not available via Composer (paid, proprietary, or not on wpackagist): Install or download the plugin, add it to src/wp-content/plugins/, and commit it to the repo. This is how ACF Pro, Schema Pro, BrightEdge, and SendGrid are managed.
Disabling MU-Plugins
Append .disabled to any mu-plugin directory name to prevent it from loading. The yax-loader skips directories with this suffix. Currently disabled:
wha-national-provider-identifier.disabled— NPI lookup functionalitywha-photo-sync.disabled— Photo sync from external source
Blocking Plugins in Production
Set the WORDPRESS_BLOCK_PLUGINS environment variable to a space-separated list of plugin slugs. The entrypoint script deletes these on every container start:
WORDPRESS_BLOCK_PLUGINS="debug-bar query-monitor rewrite-rules-inspector"