chore: initial snapshot of 2meet-data-optimizer v0.1.0

Baseline before backporting wp-data-optimizer v3.0.1-v3.4.6.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TbG1keQQ7XBa7qMQY16KCY
This commit is contained in:
2026-07-31 05:06:36 +08:00
commit d36bb954d1
206 changed files with 66538 additions and 0 deletions
@@ -0,0 +1,498 @@
<?php
/**
* Member entity field registration for WP Data Optimizer.
*
* Registers four user entity groups designed for 千萬 (10M) member scale.
* All groups use TMDO_Entity_Registry → flat tables instead of wp_usermeta EAV.
*
* @package WP_Data_Optimizer
*/
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
/**
* Registers four user entity groups for membership, activity, profile, and SSO.
*
* Groups → flat tables:
* membership → wp_wpdo_user_membership (tier, points, expiry — high-freq search)
* activity → wp_wpdo_user_activity (login counters, last-active — high-freq write)
* profile → wp_wpdo_user_profile (display fields, specialties — low-freq write)
* sso → wp_wpdo_user_sso (Hub token cache, replaces _tmso_* usermeta)
*
* Called from TMDO_Core::run() before wpdo_register_entity_fields fires.
* Pattern mirrors TMDO_WooCommerce::register() / register_user_entity_fields().
*/
final class TMDO_Member_Fields {
/**
* Hook into WPDO entity field registration.
*
* @return void
*/
public static function register(): void {
add_action( 'wpdo_register_entity_fields', array( __CLASS__, 'register_entity_fields' ) );
}
/**
* Register all user entity groups.
*
* V2.7.0: Adds four legacy-key groups (core_profile, social, commerce, hp_user)
* to absorb wp_usermeta rows that previously bypassed the entity bridge —
* driving the wp_users:wp_usermeta ratio from 1:5.5 toward 1:2.5.
*
* V2.8.4: Adds admin_prefs group — the 7 default keys WP core writes for
* EVERY new user via wp_insert_user (rich_editing, syntax_highlighting,
* comment_shortcuts, admin_color, use_ssl, show_admin_bar_front,
* dismissed_wp_pointers). Without this group, fresh users always show
* ratio 1:9 regardless of any other optimization.
*
* @return void
*/
public static function register_entity_fields(): void {
if ( ! class_exists( 'TMDO_Entity_Registry' ) ) {
return;
}
self::register_membership_group();
self::register_activity_group();
self::register_profile_group();
self::register_sso_group();
self::register_core_profile_group();
self::register_social_group();
self::register_commerce_group();
self::register_hp_user_group();
self::register_admin_prefs_group();
}
// ── Group definitions ────────────────────────────────────────────────────
/**
* Tier level, points balance, expiry — primary search target.
*
* Extra composite indexes (idx_level_expires, idx_expires_level, idx_points_bal)
* are applied by TMDO_Installer::install_member_indexes() after table creation.
*
* @return void
*/
private static function register_membership_group(): void {
TMDO_Entity_Registry::register_group(
'user',
'membership',
array(
array(
'key' => 'membership_level',
'type' => 'enum',
'searchable' => true,
'options' => array( 'bronze', 'silver', 'gold', 'platinum', 'custom' ),
'label' => 'Membership tier level',
),
array(
'key' => 'points_balance',
'type' => 'integer',
'searchable' => true,
'default' => 0,
'label' => 'Current points balance',
),
array(
'key' => 'membership_expires_at',
'type' => 'datetime',
'searchable' => true,
'label' => 'Membership expiry datetime',
),
array(
'key' => 'membership_activated_at',
'type' => 'datetime',
'label' => 'Membership activation datetime',
),
array(
'key' => 'tier_source',
'type' => 'text',
'label' => 'Tier source: manual / wc_subscription / admin_set',
),
array(
'key' => 'custom_tier_label',
'type' => 'text',
'label' => 'Custom tier display label (when level=custom)',
),
)
);
}
/**
* Login counters and last-active timestamps — separated to avoid lock
* contention with membership reads during high-traffic periods.
*
* @return void
*/
private static function register_activity_group(): void {
TMDO_Entity_Registry::register_group(
'user',
'activity',
array(
array(
'key' => 'login_count',
'type' => 'integer',
'searchable' => true,
'default' => 0,
'label' => 'Cumulative login count',
),
array(
'key' => 'last_active_at',
'type' => 'datetime',
'searchable' => true,
'label' => 'Last activity datetime',
),
array(
'key' => 'last_login_at',
'type' => 'datetime',
'label' => 'Last login datetime',
),
array(
'key' => 'last_order_at',
'type' => 'datetime',
'label' => 'Last order datetime',
),
array(
'key' => 'session_count',
'type' => 'integer',
'default' => 0,
'label' => 'Total session count',
),
array(
'key' => 'account_flags',
'type' => 'integer',
'default' => 0,
'label' => 'Bitmask: 1=email_verified 2=phone_verified 4=kyc 8=social_signup',
),
)
);
}
/**
* Display fields, specialties, avatar — written infrequently.
*
* @return void
*/
private static function register_profile_group(): void {
TMDO_Entity_Registry::register_group(
'user',
'profile',
array(
array(
'key' => 'specialties',
'type' => 'json',
'label' => 'Professional specialties (JSON array)',
),
array(
'key' => 'bio_url',
'type' => 'text',
'label' => 'Bio or portfolio URL',
),
array(
'key' => 'avatar_url',
'type' => 'text',
'label' => 'Avatar image URL',
),
array(
'key' => 'display_name_custom',
'type' => 'text',
'searchable' => true,
'fulltext' => true,
'label' => 'Custom display name (fulltext searchable)',
),
array(
'key' => 'locale',
'type' => 'text',
'label' => 'User locale (e.g. zh_TW)',
),
)
);
}
/**
* Hub/Spoke SSO token cache — replaces _tmso_* usermeta.
*
* Silent refresh fires every 15 min per user; at 10M users this is a
* high-frequency EAV hot-spot. A flat table + object cache hit cuts DB
* load 1050× versus a wp_usermeta EAV scan per refresh.
*
* last_id_token is NOT stored in plaintext (privacy + volume). Only the
* SHA-256 hash is kept for SLO token comparison.
*
* @return void
*/
private static function register_sso_group(): void {
TMDO_Entity_Registry::register_group(
'user',
'sso',
array(
array(
'key' => 'hub_global_user_id',
'type' => 'text',
'searchable' => true,
'label' => 'Hub global user UUID (2mso_user_mapping.global_user_id bridge key)',
),
array(
'key' => 'picture_url',
'type' => 'text',
'label' => 'Social / SSO profile picture URL',
),
array(
'key' => 'last_id_token_hash',
'type' => 'text',
'label' => 'SHA-256(last_id_token) for SLO comparison — no plaintext stored',
),
array(
'key' => 'refresh_token_enc',
'type' => 'textarea',
'label' => 'Encrypted refresh token (TMSO_Crypto — key-versioned enc_vN:ciphertext)',
),
array(
'key' => 'token_expires_at',
'type' => 'datetime',
'searchable' => true,
'label' => 'SSO token expiry (set to past to force re-auth on next request)',
),
array(
'key' => 'sso_last_login_at',
'type' => 'datetime',
'label' => 'Last SSO-initiated login datetime',
),
array(
'key' => 'sso_login_count',
'type' => 'integer',
'default' => 0,
'label' => 'SSO login count',
),
)
);
}
/**
* WP core user fields stored as multi-row EAV in wp_usermeta.
*
* Absorbing these here lets the Hook Bus short-circuit get_user_meta() /
* update_user_meta() for the keys WP itself uses for display_name resolution
* and the WP profile UI.
*
* @return void
*/
private static function register_core_profile_group(): void {
TMDO_Entity_Registry::register_group(
'user',
'core_profile',
array(
array(
'key' => 'nickname',
'type' => 'text',
'searchable' => true,
'label' => 'WP nickname',
),
array(
'key' => 'first_name',
'type' => 'text',
'searchable' => true,
'label' => 'WP first name',
),
array(
'key' => 'last_name',
'type' => 'text',
'searchable' => true,
'label' => 'WP last name',
),
array(
'key' => 'description',
'type' => 'textarea',
'label' => 'WP user bio',
),
)
);
}
/**
* Social profile URLs (HivePress vendor-profile + WP user-contact-methods).
*
* 15 keys × 8 vendor users ≈ 120 EAV rows in this dataset.
*
* @return void
*/
private static function register_social_group(): void {
$social_keys = array(
'facebook',
'twitter',
'instagram',
'youtube',
'tiktok',
'linkedin',
'vimeo',
'vkontakte',
'mastodon',
'medium',
'wordpress',
'odnoklassniki',
'pinterest',
'dribbble',
'github',
);
// Social URLs typed as `textarea` (TEXT) — VARCHAR(255) silently truncates
// long share URLs (utm params, deep paths) under WP's default non-strict
// SQL mode. TEXT (64KB) covers all realistic URL lengths.
$fields = array();
foreach ( $social_keys as $key ) {
$fields[] = array(
'key' => $key,
'type' => 'textarea',
'label' => ucfirst( $key ) . ' profile URL',
);
}
TMDO_Entity_Registry::register_group( 'user', 'social', $fields );
}
/**
* WooCommerce billing & shipping address fields.
*
* Billing_email is searchable for guest-checkout customer lookups.
*
* @return void
*/
private static function register_commerce_group(): void {
$address_keys = array(
'first_name',
'last_name',
'company',
'address_1',
'address_2',
'city',
'state',
'postcode',
'country',
);
$fields = array();
foreach ( $address_keys as $key ) {
$fields[] = array(
'key' => 'billing_' . $key,
'type' => 'text',
'label' => 'WC billing ' . str_replace( '_', ' ', $key ),
);
$fields[] = array(
'key' => 'shipping_' . $key,
'type' => 'text',
'label' => 'WC shipping ' . str_replace( '_', ' ', $key ),
);
}
// Email + phone are billing-only.
$fields[] = array(
'key' => 'billing_email',
'type' => 'text',
'searchable' => true,
'label' => 'WC billing email',
);
$fields[] = array(
'key' => 'billing_phone',
'type' => 'text',
'label' => 'WC billing phone',
);
$fields[] = array(
'key' => 'shipping_phone',
'type' => 'text',
'label' => 'WC shipping phone',
);
TMDO_Entity_Registry::register_group( 'user', 'commerce', $fields );
}
/**
* HivePress per-user fields (favorites + avatar attachment).
*
* Hp_favorited_listings is a serialized array of post IDs in legacy storage;
* the migration engine safe_unserialize()s it (allowed_classes=false to block
* PHP-object injection) then the json type encodes back to a JSON array column.
*
* @return void
*/
private static function register_hp_user_group(): void {
TMDO_Entity_Registry::register_group(
'user',
'hp_user',
array(
array(
'key' => 'hp_favorited_listings',
'type' => 'json',
'label' => 'HivePress favorited listing IDs (array)',
),
array(
'key' => 'hp_image',
'type' => 'text',
'label' => 'HivePress avatar attachment ID',
),
)
);
}
/**
* WP-core admin pref defaults — written by `wp_insert_user()` for EVERY
* new user regardless of role. Without registering these, a fresh user
* lands at ratio 1:9 (7 admin prefs + wp_capabilities + wp_user_level).
* Once registered, Hook Bus intercepts `update_user_meta()` calls from
* `wp_insert_user()` and routes them to `wp_wpdo_user_admin_prefs` flat
* table → fresh user ratio drops to 1:2.
*
* Values are stored as text because WP itself stores 'true'/'false'
* strings (not booleans), 'fresh' / 'classic' (admin_color enum strings),
* and integer-as-string for `use_ssl`. Preserving WP's textual storage
* shape ensures downstream code (e.g. theme switchers reading
* `admin_color`) sees the exact same value as before.
*
* @since 2.8.4
* @return void
*/
private static function register_admin_prefs_group(): void {
TMDO_Entity_Registry::register_group(
'user',
'admin_prefs',
array(
array(
'key' => 'rich_editing',
'type' => 'text',
'label' => 'Visual editor enabled (true/false string)',
),
array(
'key' => 'syntax_highlighting',
'type' => 'text',
'label' => 'Code editor syntax highlighting (true/false string)',
),
array(
'key' => 'comment_shortcuts',
'type' => 'text',
'label' => 'Comment moderation keyboard shortcuts (true/false string)',
),
array(
'key' => 'admin_color',
'type' => 'text',
'label' => 'Admin colour scheme (fresh/classic/etc)',
),
array(
'key' => 'use_ssl',
'type' => 'text',
'label' => 'Force SSL on admin (0/1 as string)',
),
array(
'key' => 'show_admin_bar_front',
'type' => 'text',
'label' => 'Show admin bar on front-end (true/false string)',
),
array(
'key' => 'dismissed_wp_pointers',
'type' => 'textarea',
'label' => 'Comma-separated dismissed pointer IDs',
),
)
);
}
}