Extending mam-main: developer guide

Goal

Build a sibling plugin that hooks into mam-main’s pipelines without modifying mam-main itself.


The pattern every sibling follows

Every well-behaved MAM Suite sibling plugin (mam-geodirectory, mam-chat-manager, mam-special-offers, etc.) follows the same shape:

  1. Plugin file with header — standard WP plugin metadata
  2. Activation gate — check that mam-main is at the required version, and that the customer is entitled
  3. Bootstrap on plugins_loaded — only after the entitlement check passes
  4. Register hooks for the surfaces this plugin extends
  5. Don’t import mam-main classes — extend through filters and actions only

Bootstrap skeleton

<?php
/**
 * Plugin Name: MAM My Plugin
 * Description: Adds X to the MAM Suite mobile app.
 * Version:     1.0.0
 */

if ( ! defined( 'ABSPATH' ) ) exit;

add_action( 'plugins_loaded', function () {

    // 1. Check mam-main is present and at required version.
    $required_version = '1.9.1';
    if ( ! apply_filters( 'mam_check_required_version', false, 'mam-main', $required_version ) ) {
        return;
    }

    // 2. Check entitlement.
    $entitlement = apply_filters( 'mam_plugin_entitlement', false, 'mam-my-plugin' );
    if ( ! $entitlement || ! empty( $entitlement['blocked'] ) ) {
        return;
    }

    // 3. Bootstrap.
    new MAM_My_Plugin_Manager();

}, 11 );  // priority 11 to ensure mam-main has loaded its register_*() chain

Common extension surfaces

Surface Hook Recipe
Inject data into the mobile JSON mam_get_phone_data_before_send Hook: mam_get_phone_data_before_send
Add a custom notification type mam_notification_list Recipe: Register a notification type
Send a notification mam_notification_send_message Hook: mam_notification_send_message
Add a tab-bar button mam_main_add_tab_bar_item_{slug} + a content-class registration Hook: mam_main_add_tab_baritem{slug}
Hide a tab-bar button per item mam_main_skip_tab_bar_button Hook: mam_main_skip_tab_bar_button
Register a custom field type mam_form_manager_process_field_type_{type} Recipe: Register a custom field type
Register a custom (non-GF) form mam_form_manager_get_forms_from_plugins Hook: mam_form_manager_get_forms_from_plugins
Register a scheduled cron task mam_cron_manager Recipe: Register a scheduled cron task
Add a content class (button type) $local_app_content registration + class Recipe: Register a content class
Inject into the publish payload mam_fastlane_settings Hook: mam_fastlane_settings
Inject extra profile fields mam_add_fields_to_user_profile Hook: mam_add_fields_to_user_profile
Append entries to the settings menu mam_main_content_class_settings Hook: mam_main_content_class_settings
React to favorites toggle mam_manage_favorites Hook: mam_manage_favorites

What you DON’T do

  • ❌ Import or require_once mam-main class files
  • ❌ Read or write mam-main’s owned options directly (use the filters)
  • ❌ Touch mam-main’s owned tables directly (use repositories where possible)
  • ❌ Rename frozen contracts (you’d break the apps in the field)
  • ❌ Fire mam-main’s hooks from outside mam-main (with a few documented exceptions like mam_notification_send_message)
  • ❌ Construct option keys yourself for per-role / per-button settings — use the cascade filters

Conventions

  • Plugin slug prefixed with mam- (e.g., mam-my-plugin)
  • Notification slugs prefixed with the plugin slug (mam-my-plugin-event_name)
  • Cron ids prefixed (mam_my_plugin_nightly_sync)
  • Custom hook names prefixed (mam_my_plugin_*)
  • Option keys prefixed (mam_my_plugin_*) — and use the migration framework for any rename
  • Class names prefixed (MAM_My_Plugin_* or mam_my_plugin_*)
  • *Stay out of the `local-app-,tsl_, andmam-` core namespaces**

Performance discipline

  • Hot path: mam_app_settings_get_setting runs 100+ times per phone-data build. Don’t add slow subscribers without object cache.
  • Hot path: mam_get_phone_data_before_send fires once per request, but contributing 50ms per subscriber across 70 subscribers = 3.5s of latency. Be cheap.
  • Cache aggressivelywp_cache_get / wp_cache_set, transients, static per-request caches.
  • Never make HTTP calls in the phone-data path without aggressive caching.
  • Use the cursor mechanism — when your data hasn’t changed, return null for your section and let the app keep its cache.

Versioning

  • mam-main uses semver-ish versions (2.1.11)
  • Bump your plugin’s version when you ship a behavior change; the WPMAM update mechanism (mam-main-software-manager-api) pulls updates
  • Document the minimum mam-main version your plugin requires

Testing your plugin

  • The Previewer app is your primary test surface
  • tests/snapshot/snapshot-phone-data.php (mam-main’s regression harness) captures JSON fixtures — useful for verifying you don’t accidentally change a section you don’t own
  • For form submissions, the Outbox Viewer under Notifications shows what was dispatched
  • For push, watch APNs / FCM dashboards plus MAM_PushResult->errors

  • Plugin: mam-main
  • Architecture overview
  • Frozen public contracts reference
  • Recipe: Register a notification type
  • Recipe: Register a custom field type
  • Recipe: Register a content class
  • Recipe: Register a scheduled cron task
  • Recipe: Register a tab-bar button
  • Hook: mam_get_phone_data_before_send
  • Hook: mam_notification_send_message
  • Hook: mam_app_settings_get_setting
  • Hook: mam_plugin_entitlement

Metadata

Field Value
Article type Recipe (Developer)
Plugin slug mam-main
Applies to plugin version 2.1.11+
Category Extending MAM Suite
Audience PHP developer
Estimated time varies (1–8 hours per plugin)
Last verified 2026-05-02
Contents

    Need Support?

    Can’t find the answer you’re looking for? Don’t worry we’re here to help!