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_data — frozen 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_arraykeys you don’t own. Two subscribers fighting overlistingsproduces last-writer-wins. - Don’t re-implement the cursor mechanism. Use
JSON_Cursor_Manager::get_cursor()and the section-levelnullsignal. - Output shaping runs after this filter.
mam_replace_null_with_empty_string()will convert your field-levelnullvalues to"". If you’re usingnullto 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_cachingoverrides 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.
Related articles
- 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 |
