Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
89.47% covered (warning)
89.47%
34 / 38
77.78% covered (warning)
77.78%
7 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
SiteCapabilities
89.47% covered (warning)
89.47%
34 / 38
77.78% covered (warning)
77.78%
7 / 9
21.51
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 update
80.00% covered (warning)
80.00%
4 / 5
0.00% covered (danger)
0.00%
0 / 1
3.07
 set
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 all
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
4
 clear
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 exists
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 is_valid_capabilities
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 fetch
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2
3namespace NewfoldLabs\WP\Module\Data;
4
5use NewfoldLabs\WP\Module\Data\API\Capabilities;
6use NewfoldLabs\WP\Module\Data\Helpers\Transient;
7
8/**
9 * Class SiteCapabilities
10 *
11 * Class that handles fetching, caching, and checking of site capabilities.
12 *
13 * @package NewfoldLabs\WP\Module\Data
14 */
15class SiteCapabilities {
16
17    /**
18     * Capabilities returned by Hiive always include this key.
19     *
20     * Used to distinguish a real Hiive payload from legacy bootstrap fallback
21     * entries that only contained a subset of capabilities.
22     */
23    public const HIIVE_RESPONSE_MARKER = 'canAccessAI';
24
25    /**
26     * Implementation of transient functionality which uses the WordPress options table when an object cache is present.
27     *
28     * @var Transient
29     */
30    protected $transient;
31
32    /**
33     * Hiive connection manager
34     *
35     * @var HiiveConnection
36     */
37    protected $hiive;
38
39    /**
40     * Constructor.
41     *
42     * @param ?Transient       $transient Inject instance of Transient class.
43     * @param ?HiiveConnection $hiive Inject instance of the hiive connection manager.
44     */
45    public function __construct( ?Transient $transient = null, ?HiiveConnection $hiive = null ) {
46        $this->transient = $transient ?? new Transient();
47        $this->hiive     = $hiive ?? new HiiveConnection();
48    }
49
50    /**
51     * Get the value of a capability.
52     *
53     * @used-by \NewfoldLabs\WP\Module\AI\SiteGen\SiteGen::check_capabilities()
54     * @used-by \NewfoldLabs\WP\Module\AI\Utils\AISearchUtil::check_capabilities()
55     * @used-by \NewfoldLabs\WP\Module\AI\Utils\AISearchUtil::check_help_capability()
56     * @used-by \NewfoldLabs\WP\Module\ECommerce\ECommerce::__construct()
57     * @used-by \NewfoldLabs\WP\Module\HelpCenter\CapabilityController::get_capability()
58     * @used-by \NewfoldLabs\WP\Module\Onboarding\Data\Config::get_site_capability()
59     *
60     * @param string $capability Capability name.
61     */
62    public function get( string $capability ): bool {
63        return $this->exists( $capability ) && $this->all()[ $capability ];
64    }
65
66    /**
67     * Merge a new list of capabilities into the existing list and save.
68     *
69     * @used-by Capabilities::update()
70     *
71     * @param array<string, bool> $capabilities The capabilities array.
72     *
73     * @return bool True if the value was changed, false otherwise.
74     */
75    public function update( array $capabilities ): bool {
76        $existing = $this->all( false );
77
78        if ( ! $this->is_valid_capabilities( $existing ) && HiiveConnection::is_connected() ) {
79            $existing = $this->fetch();
80        }
81
82        $updated_capabilities = array_merge( $existing, $capabilities );
83
84        return $this->set( $updated_capabilities );
85    }
86
87    /**
88     * Save a list of capabilities, overwriting the existing list.
89     *
90     * @used-by self::fetch()
91     * @used-by Capabilities::update()
92     *
93     * @param array<string, bool> $capabilities The capabilities array.
94     *
95     * @return bool True if the value was set, false otherwise.
96     */
97    public function set( array $capabilities ): bool {
98        if ( ! $this->is_valid_capabilities( $capabilities ) ) {
99            return false;
100        }
101
102        return $this->transient->set( 'nfd_site_capabilities', $capabilities, 4 * constant( 'HOUR_IN_SECONDS' ) );
103    }
104
105    /**
106     * Get all capabilities.
107     *
108     * @param bool $fetch_when_absent Make a request to Hiive to fetch capabilities when not present in cache (default: `true`).
109     *
110     * @return array<string, bool> List of capabilities and if they are enabled or not.
111     */
112    public function all( bool $fetch_when_absent = true ): array {
113        $capabilities = $this->transient->get( 'nfd_site_capabilities' );
114
115        if ( $this->is_valid_capabilities( $capabilities ) ) {
116            return $capabilities;
117        }
118
119        if ( $fetch_when_absent ) {
120            $fetched = $this->fetch();
121
122            if ( $this->is_valid_capabilities( $fetched ) ) {
123                $this->set( $fetched );
124                return $fetched;
125            }
126
127            return array();
128        }
129
130        return array();
131    }
132
133    /**
134     * Remove any cached capabilities.
135     *
136     * @used-by HiiveConnection::connect()
137     *
138     * @return bool Whether the cache entry was deleted.
139     */
140    public function clear(): bool {
141        return $this->transient->delete( 'nfd_site_capabilities' );
142    }
143
144    /**
145     * Check if a capability exists.
146     *
147     * @param string $capability Capability name.
148     */
149    protected function exists( string $capability ): bool {
150        return array_key_exists( $capability, $this->all() );
151    }
152
153    /**
154     * Whether a capabilities array looks like a real Hiive response.
155     *
156     * @param mixed $capabilities Capabilities array candidate.
157     */
158    protected function is_valid_capabilities( $capabilities ): bool {
159        return is_array( $capabilities )
160            && ! empty( $capabilities )
161            && array_key_exists( self::HIIVE_RESPONSE_MARKER, $capabilities );
162    }
163
164    /**
165     * Fetch all capabilities from Hiive.
166     *
167     * @return array<string, bool>
168     */
169    protected function fetch(): array {
170
171        $response = $this->hiive->hiive_request(
172            'sites/v1/capabilities',
173            null,
174            array(
175                'method' => 'GET',
176            )
177        );
178
179        if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
180            return array();
181        }
182
183        $body = wp_remote_retrieve_body( $response );
184        $data = json_decode( $body, true );
185
186        return is_array( $data ) ? $data : array();
187    }
188}