prefix . 'aieo_dmm_user_fp'; if (!self::table_exists($table)) { return self::$l1[$user_id] = self::compute_guest(); } $row = $wpdb->get_row($wpdb->prepare("SELECT * FROM {$table} WHERE user_id = %d", $user_id), ARRAY_A); $current_key = self::current_version_key(); if (!$row || !isset($row['fp_version_key']) || $row['fp_version_key'] !== $current_key) { $row = self::compute($user_id, $current_key); } else { $row = self::hydrate_row($row); } self::$l1[$user_id] = $row; return $row; } public static function compute($user_id, $version_key = null) { $user_id = (int) $user_id; if ($user_id <= 0) { return self::compute_guest(); } if ($version_key === null) { $version_key = self::current_version_key(); } $user = get_userdata($user_id); if (!$user) { return self::compute_guest(); } $roles = is_array($user->roles) ? $user->roles : array(); sort($roles); $role_hash = substr(md5(implode(',', $roles)), 0, 12); $enabled_roles = array(); if (class_exists('AIEO_DMM_Settings')) { $enabled_roles = (array) AIEO_DMM_Settings::get('enabled_roles', array()); } $enabled_role_column = ''; foreach ($roles as $r) { if (in_array($r, $enabled_roles, true)) { $clean = strtolower(preg_replace('/[^a-z0-9_]/', '_', $r)); if ($clean === '') { $clean = 'r'; } if (preg_match('/^[0-9]/', $clean)) { $clean = 'r_' . $clean; } $enabled_role_column = $clean; break; } } list($loyalty_class_ids, $loyalty_sum) = self::compute_loyalty($user_id); $data = array( 'user_id' => $user_id, 'role_hash' => $role_hash, 'enabled_role_column' => $enabled_role_column, 'loyalty_class_ids' => wp_json_encode($loyalty_class_ids), 'loyalty_extra_pct_sum' => (float) $loyalty_sum, 'personal_extra_pct' => 0.0, 'brand_overrides' => wp_json_encode(new stdClass()), 'sequence_pct' => 0.0, 'sequence_expires_at' => null, 'fp_version_key' => (string) $version_key, 'computed_at' => current_time('mysql', true), ); $formats = array('%d', '%s', '%s', '%s', '%f', '%f', '%s', '%f', '%s', '%s', '%s'); global $wpdb; $table = $wpdb->prefix . 'aieo_dmm_user_fp'; $wpdb->replace($table, $data, $formats); return self::hydrate_row($data); } private static function compute_guest() { return self::hydrate_row(array( 'user_id' => 0, 'role_hash' => substr(md5('guest'), 0, 12), 'enabled_role_column' => '', 'loyalty_class_ids' => '[]', 'loyalty_extra_pct_sum' => 0.0, 'personal_extra_pct' => 0.0, 'brand_overrides' => '{}', 'sequence_pct' => 0.0, 'sequence_expires_at' => null, 'fp_version_key' => self::current_version_key(), 'computed_at' => current_time('mysql', true), )); } public static function on_user_change($user_id) { $user_id = (int) $user_id; if ($user_id <= 0) { return; } self::compute($user_id); } public static function clear_memo() { self::$l1 = array(); } private static function compute_loyalty($user_id) { global $wpdb; $assign = $wpdb->prefix . 'aieo_dmm_loyalty_assignments'; $defs = $wpdb->prefix . 'aieo_dmm_loyalty_classes'; if (!self::table_exists($assign) || !self::table_exists($defs)) { return array(array(), 0.0); } $rows = $wpdb->get_results($wpdb->prepare( "SELECT d.id, d.extra_percent FROM {$assign} a INNER JOIN {$defs} d ON d.id = a.class_id WHERE a.user_id = %d AND d.enabled = 1", $user_id ), ARRAY_A); $ids = array(); $sum = 0.0; if (is_array($rows)) { foreach ($rows as $r) { $ids[] = (int) $r['id']; $sum += (float) $r['extra_percent']; } } return array($ids, $sum); } private static function current_version_key() { $domains = array('brands', 'gifts', 'loyalty', 'prices', 'needs', 'shipping'); $parts = array(); foreach ($domains as $d) { $parts[] = (int) get_option('aieo_dmm_cache_v_' . $d, 1); } return implode(':', $parts); } private static function hydrate_row($row) { if (!is_array($row)) { return null; } $row['user_id'] = (int) ($row['user_id'] ?? 0); $row['role_hash'] = (string) ($row['role_hash'] ?? ''); $row['enabled_role_column'] = (string) ($row['enabled_role_column'] ?? ''); $row['loyalty_extra_pct_sum'] = (float) ($row['loyalty_extra_pct_sum'] ?? 0); $row['personal_extra_pct'] = (float) ($row['personal_extra_pct'] ?? 0); $row['sequence_pct'] = (float) ($row['sequence_pct'] ?? 0); $row['loyalty_class_ids'] = self::decode_int_list($row['loyalty_class_ids'] ?? ''); $decoded_overrides = is_array($row['brand_overrides'] ?? null) ? $row['brand_overrides'] : (is_string($row['brand_overrides'] ?? '') ? json_decode((string) $row['brand_overrides'], true) : null); $row['brand_overrides'] = is_array($decoded_overrides) ? $decoded_overrides : array(); return $row; } private static function decode_int_list($raw) { if (is_array($raw)) { return array_map('intval', $raw); } if (!is_string($raw) || $raw === '') { return array(); } $decoded = json_decode($raw, true); return is_array($decoded) ? array_map('intval', $decoded) : array(); } private static function table_exists($table) { static $memo = array(); if (isset($memo[$table])) { return $memo[$table]; } global $wpdb; $found = $wpdb->get_var($wpdb->prepare( "SELECT COUNT(*) FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = %s", $table )); return $memo[$table] = ((int) $found) > 0; } }