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
12declare(strict_types=1);
13
14namespace SwapAds\Client\Audience;
15
16use SwapAds\Client\Api\RestClient;
17use SwapAds\Client\License\LicenseManager;
18
19/**
20 * Class SiteAudience.
21 *
22 * @since 1.0.0
23 */
24final 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}