PluginProbe
AI / 0.2.0
AI v0.2.0
1.3.0 1.2.0 1.1.0 1.0.2 1.0.1 1.0.0 0.9.0 trunk 0.1.1 0.2.0 0.2.1 0.3.0 0.3.1 0.4.0 0.4.1 0.5.0 0.6.0 0.7.0 0.8.0
ai / includes / Abstracts / Abstract_Experiment.php

Abstract_Experiment.php in AI 0.2.0, at includes/Abstracts/Abstract_Experiment.php

234 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abstract Experiment base class.
4 *
5 * @package WordPress\AI\Abstracts
6 */
7
8 declare( strict_types=1 );
9
10 namespace WordPress\AI\Abstracts;
11
12 use WordPress\AI\Contracts\Experiment;
13 use WordPress\AI\Exception\Invalid_Experiment_Metadata_Exception;
14 use WordPress\AI\Settings\Settings_Registration;
15
16 /**
17 * Base implementation for experiments.
18 *
19 * Provides common functionality for all experiments including enable/disable state.
20 *
21 * @since 0.1.0
22 */
23 abstract class Abstract_Experiment implements Experiment {
24 /**
25 * Experiment identifier.
26 *
27 * @since 0.1.0
28 * @var string
29 */
30 protected string $id;
31
32 /**
33 * Experiment label.
34 *
35 * @since 0.1.0
36 * @var string
37 */
38 protected string $label;
39
40 /**
41 * Experiment description.
42 *
43 * @since 0.1.0
44 * @var string
45 */
46 protected string $description;
47
48 /**
49 * Cache for this experiment's enabled status.
50 *
51 * @since 0.1.0
52 * @var bool|null
53 */
54 private ?bool $enabled_cache = null;
55
56 /**
57 * Constructor.
58 *
59 * Loads experiment metadata and initializes properties.
60 *
61 * @since 0.1.0
62 *
63 * @throws \WordPress\AI\Exception\Invalid_Experiment_Metadata_Exception If experiment metadata is invalid.
64 */
65 final public function __construct() {
66 $metadata = $this->load_experiment_metadata();
67
68 if ( empty( $metadata['id'] ) ) {
69 throw new Invalid_Experiment_Metadata_Exception(
70 esc_html__( 'Experiment id is required in load_experiment_metadata().', 'ai' )
71 );
72 }
73
74 if ( empty( $metadata['label'] ) ) {
75 throw new Invalid_Experiment_Metadata_Exception(
76 esc_html__( 'Experiment label is required in load_experiment_metadata().', 'ai' )
77 );
78 }
79
80 if ( empty( $metadata['description'] ) ) {
81 throw new Invalid_Experiment_Metadata_Exception(
82 esc_html__( 'Experiment description is required in load_experiment_metadata().', 'ai' )
83 );
84 }
85
86 $this->id = $metadata['id'];
87 $this->label = $metadata['label'];
88 $this->description = $metadata['description'];
89 }
90
91 /**
92 * Loads experiment metadata.
93 *
94 * Must return an array with keys: id, label, description.
95 *
96 * @since 0.1.0
97 *
98 * @return array{id: string, label: string, description: string} Experiment metadata.
99 */
100 abstract protected function load_experiment_metadata(): array;
101
102 /**
103 * Gets the experiment ID.
104 *
105 * @since 0.1.0
106 *
107 * @return string Experiment identifier.
108 */
109 public function get_id(): string {
110 return $this->id;
111 }
112
113 /**
114 * Gets the experiment label.
115 *
116 * @since 0.1.0
117 *
118 * @return string Translated experiment label.
119 */
120 public function get_label(): string {
121 return $this->label;
122 }
123
124 /**
125 * Gets the experiment description.
126 *
127 * @since 0.1.0
128 *
129 * @return string Translated experiment description.
130 */
131 public function get_description(): string {
132 return $this->description;
133 }
134
135 /**
136 * Checks if experiment is enabled.
137 *
138 * Experiments require both the global toggle and individual experiment toggle to be enabled.
139 * Results are cached per instance to avoid redundant option lookups and filter calls.
140 *
141 * @since 0.1.0
142 *
143 * @return bool True if enabled, false otherwise.
144 */
145 final public function is_enabled(): bool {
146 // Return cached result if available.
147 if ( null !== $this->enabled_cache ) {
148 return $this->enabled_cache;
149 }
150
151 // Check global experiments toggle first.
152 $global_enabled = (bool) get_option( Settings_Registration::GLOBAL_OPTION, false );
153 if ( ! $global_enabled ) {
154 $this->enabled_cache = false;
155 return false;
156 }
157
158 // Check experiment-specific option.
159 $experiment_enabled = (bool) get_option( "ai_experiment_{$this->id}_enabled", false );
160
161 /**
162 * Filters the enabled status for a specific experiment.
163 *
164 * The dynamic portion of the hook name, `$this->id`, refers to the experiment ID.
165 *
166 * @since 0.1.0
167 *
168 * @param bool $experiment_enabled Whether the experiment is enabled.
169 */
170 $is_enabled = (bool) apply_filters( "ai_experiments_experiment_{$this->id}_enabled", $experiment_enabled );
171
172 // Cache the result.
173 $this->enabled_cache = $is_enabled;
174
175 return $is_enabled;
176 }
177
178 /**
179 * Registers experiment-specific settings.
180 *
181 * Override this method in child classes to register custom settings options
182 * using WordPress Settings API (register_setting).
183 *
184 * @since 0.1.0
185 *
186 * @return void
187 */
188 public function register_settings(): void {
189 // Default implementation does nothing.
190 // Child classes can override to register custom settings.
191 }
192
193 /**
194 * Renders experiment-specific settings fields.
195 *
196 * Override this method in child classes to render custom settings UI
197 * that will appear within the experiment's card on the settings page.
198 * This is called after the experiment's main toggle control.
199 *
200 * @since 0.1.0
201 *
202 * @return void
203 */
204 public function render_settings_fields(): void {
205 // Default implementation does nothing.
206 // Child classes can override to render custom settings UI.
207 }
208
209 /**
210 * Gets the option name for a custom experiment setting field.
211 *
212 * Generates a properly namespaced option name for experiment-specific settings.
213 * Use this when registering and rendering custom settings fields to ensure
214 * consistent naming across the plugin.
215 *
216 * @since 0.1.0
217 *
218 * @param string $option_name The base option name (e.g., 'api_key', 'temperature').
219 * @return string The fully namespaced option name.
220 */
221 final protected function get_field_option_name( string $option_name ): string {
222 return "ai_experiment_{$this->id}_field_{$option_name}";
223 }
224
225 /**
226 * Registers the experiment.
227 *
228 * Must be implemented by child classes to set up hooks and functionality.
229 *
230 * @since 0.1.0
231 */
232 abstract public function register(): void;
233 }
234