| Code Coverage
| Lines | Functions and Methods | Classes and Traits
Total | 61.54% covered (warning) | 61.54% | 16 / 26 | 66.67% covered (warning) | 66.67% | 2 / 3 | CRAP | 0.00% covered (danger) | 0.00% | 0 / 1
SiteAudience | 61.54% covered (warning) | 61.54% | 16 / 26 | 66.67% covered (warning) | 66.67% | 2 / 3 | 11.64 | 0.00% covered (danger) | 0.00% | 0 / 1
get | 100.00% covered (success) | 100.00% | 6 / 6 | 100.00% covered (success) | 100.00% | 1 / 1 | 1 |
save | 44.44% covered (danger) | 44.44% | 8 / 18 | 0.00% covered (danger) | 0.00% | 0 / 1 | 9.29 |
isDeclared | 100.00% covered (success) | 100.00% | 2 / 2 | 100.00% covered (success) | 100.00% | 1 / 1 | 2 |

1 | <?php
2 | /**
3 | * Client-side Audience Settings.
4 | *
5 | * Stores the operator's audience selection in WP options, syncs to server
6 | * via /audience/set. Server is authoritative; client is just cache.
7 | *
8 | * @package SwapAds\Client\Audience
9 | * @since 1.0.0
10 | */
11 |
12 | declare(strict_types=1);
13 |
14 | namespace SwapAds\Client\Audience;
15 |
16 | use SwapAds\Client\Api\RestClient;
17 | use SwapAds\Client\License\LicenseManager;
18 |
19 | /**
20 | * Class SiteAudience.
21 | *
22 | * @since 1.0.0
23 | */
24 | final class SiteAudience
25 | {
26 | public const OPTION_NICHE_CODE = 'swapads_client_audience_niche_code'; // F235: single-niche Y1
27 | public const OPTION_SUB_NICHES = 'swapads_client_audience_sub_niches';
28 | public const OPTION_GEO_BUCKETS = 'swapads_client_audience_geo_buckets';
29 | public const OPTION_TRAFFIC_RANGES = 'swapads_client_audience_traffic_ranges';
30 | public const OPTION_LAST_SYNC = 'swapads_client_audience_last_sync';
31 |
32 | /**
33 | * Get audience settings.
34 | *
35 | * @return array<string, array<int, string>>
36 | */
37 | public static function get(): array
38 | {
39 | return [
40 | 'niche_code' => (string) get_option(self::OPTION_NICHE_CODE, ''), // F235
41 | 'sub_niches' => (array) get_option(self::OPTION_SUB_NICHES, []),
42 | 'geo_buckets' => (array) get_option(self::OPTION_GEO_BUCKETS, []),
43 | 'traffic_ranges' => (array) get_option(self::OPTION_TRAFFIC_RANGES, []),
44 | ];
45 | }
46 |
47 | /**
48 | * Save audience settings locally + push to server.
49 | *
50 | * @param array<int, string> $niches
51 | * @param array<int, string> $subNiches
52 | * @param array<int, string> $geoBuckets
53 | * @param array<int, string> $trafficRanges
54 | *
55 | * @return array<string, mixed> Server response.
56 | */
57 | /**
58 | * Save audience settings locally + push to server.
59 | *
60 | * @since 1.0.0
61 | * @since 1.5.2 F235: accept $nicheCode (single-niche Y1 selection).
62 | * First param $niches is deprecated (kept for back-compat).
63 | */
64 | public static function save(?string $nicheCode, array $subNiches, array $geoBuckets, array $trafficRanges): array
65 | {
66 | $nicheCode = ($nicheCode !== null && $nicheCode !== '') ? $nicheCode : null;
67 | update_option(self::OPTION_NICHE_CODE, $nicheCode);
68 | update_option(self::OPTION_SUB_NICHES, array_values(array_unique($subNiches)));
69 | update_option(self::OPTION_GEO_BUCKETS, array_values(array_unique($geoBuckets)));
70 | update_option(self::OPTION_TRAFFIC_RANGES, array_values(array_unique($trafficRanges)));
71 | // Push to server only if licensed
72 | if (!LicenseManager::isLicensed()) {
73 | update_option(self::OPTION_LAST_SYNC, current_time('mysql'));
74 | return ['success' => true, 'local_only' => true];
75 | }
76 | // Push to server
77 | $client = RestClient::fromOption();
78 | $result = $client->post('/wp-json/swapads-server/v1/audience/set', [
79 | 'niche_code' => $nicheCode,
80 | 'sub_niches' => $subNiches,
81 | 'geo_buckets' => $geoBuckets,
82 | 'traffic_ranges' => $trafficRanges,
83 | ]);
84 | if (($result['http_status'] ?? 0) === 200) {
85 | update_option(self::OPTION_LAST_SYNC, current_time('mysql'));
86 | }
87 | return $result;
88 | }
89 |
90 | /**
91 | * Check if audience has been declared.
92 | *
93 | * @return bool
94 | */
95 | public static function isDeclared(): bool
96 | {
97 | $aud = self::get();
98 | return !empty($aud['niche_code']) || !empty($aud['sub_niches']);
99 | }
100 | }