Hook: mam_get_phone_data_before_send

Purpose

The primary mobile-API extension point. ~70 sibling plugins (mam-geodirectory, mam-chat-manager, mam-special-offers, mam-forms-manager, mam-inapp-purchase-manager, etc.) inject their data into the phone-data response by subscribing to this filter.

Fired in phase_content of MAM_Phone_Data_Pipeline — once per pipeline run, with the assembled $data_array as input.

⚠️ Frozen contract — do not rename. Renaming this filter would silently break ~70 sibling plugins.


Signature

$data_array = apply_filters(
    'mam_get_phone_data_before_send',
    array  $data_array,
    string $cursor                    // optional, varies by version
);
Parameter Type Description
$data_array array The accumulated phone-data response. Top-level keys include user, settings, main_button_array, tab_bar_settings, form_data, nonce, home_cats (when applicable), and any keys previous subscribers have injected.
$cursor string/int The cursor value the mobile app sent. Use to short-circuit your section if it hasn’t changed.

Returns: array — the (possibly augmented) $data_array.


Priority conventions

Priority Use Example subscribers
1 Base settings mam_app_settings::manage_phone_data
10 Default cohort — content providers most sibling plugins
99–100 Late-running enrichments mam-faceted-search (100), mam-localize (99)
199 Form-state validation mam_user_role_form_manager::process_last_json_check
999+ Niche overrides mam-nws-weather (999), payment processors
1000 home_cats injection MAM_Main_Manager::manage_phone_datafrozen contract
1001+ Use-case total overrides mam-meal-genie (9999), mam-medicoach (9999)

See Priority conventions for phone-data subscribers.


Example: minimal subscriber (priority 10)

add_filter( 'mam_get_phone_data_before_send', function ( array $data_array ): array {

    $data_array['my_plugin_widget'] = array(
        'enabled'    => true,
        'message'    => 'Hello from my plugin',
        'items'      => $this->get_items_for_user( mam_current_request()->user_id() ),
    );

    return $data_array;
}, 10 );

The injected key (my_plugin_widget) appears at the top level of the JSON response.


Example: cache-aware subscriber

add_filter( 'mam_get_phone_data_before_send', function ( array $data_array ): array {

    $cursor      = JSON_Cursor_Manager::get_cursor( get_current_user_id() );
    $section_ts  = (int) get_option( 'my_plugin_data_changed_at', 0 );

    if ( $section_ts <= $cursor ) {
        $data_array['my_plugin_widget'] = null;   // signal "no change"
        return $data_array;
    }

    $data_array['my_plugin_widget'] = $this->build_widget();
    return $data_array;
}, 10 );

When you mutate underlying data, bump my_plugin_data_changed_at to invalidate the per-user cache.


Example: use-case total override (priority 9999)

// Only register this on use-case sites — not in a base sibling plugin.
add_filter( 'mam_get_phone_data_before_send', function ( array $data_array ): array {

    if ( ! $this->is_my_use_case_site() ) {
        return $data_array;
    }

    // Replace specific sections wholesale.
    $data_array['main_button_array'] = $this->build_use_case_buttons();
    $data_array['home_cats']         = $this->build_use_case_home_cats();

    return $data_array;
}, 9999 );

⚠️ Use-case overrides at 9999 run AFTER home_cats injection (priority 1000). If you replace home_cats, the priority-1000 subscriber’s work is overwritten — that’s intentional in use-case scenarios.


Gotchas

  • Hot path. This filter fires on every phone-data request. Slow subscribers degrade app-load latency for every user.
  • Don’t make HTTP calls from a subscriber unless you cache aggressively. A 500ms HTTP call adds 500ms to every app launch.
  • Don’t overwrite $data_array keys you don’t own. Two subscribers fighting over listings produces last-writer-wins.
  • Don’t re-implement the cursor mechanism. Use JSON_Cursor_Manager::get_cursor() and the section-level null signal.
  • Output shaping runs after this filter. mam_replace_null_with_empty_string() will convert your field-level null values to "". If you’re using null to signal “no change”, that signal is at the section root, not inside fields.
  • Priority 1000 is reserved for home_cats. Don’t register at 1000.
  • Cloning admins always rebuild — your cache short-circuit logic should still run, but bypass_caching overrides the cursor comparison.
  • Tracer integration. If you do something interesting, fire do_action('add_to_tracer', 'my-section-built') so the debug handler can profile.

  • Phone data pipeline overview
  • Phone data pipeline phases
  • Mobile JSON shape
  • Cursor cache mechanism
  • Priority conventions for phone-data subscribers
  • Hook: mam_local_app_data
  • Hook: mam_main_check_initial_form
  • Frozen public contracts reference

Metadata

Field Value
Article type Hook Reference
Plugin slug mam-main
Applies to plugin version 2.1.11+
Hook type filter
Audience PHP developer
Frozen contract yes — ~70 subscribers, ~2K customer sites
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!