| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Connectors API: WP_Connector_Registry class. 4 * 5 * @package WordPress 6 * @subpackage Connectors 7 * @since 7.0.0 8 */ 9 10 /** 11 * Manages the registration and lookup of connectors. 12 * 13 * This is an internal class. Use the public API functions to interact with connectors: 14 * 15 * - `wp_is_connector_registered()` — check if a connector exists. 16 * - `wp_get_connector()` — retrieve a single connector's data. 17 * - `wp_get_connectors()` — retrieve all registered connectors. 18 * 19 * Plugins receive the registry instance via the `wp_connectors_init` action 20 * to register or override connectors directly. 21 * 22 * @since 7.0.0 23 * @access private 24 * 25 * @see wp_is_connector_registered() 26 * @see wp_get_connector() 27 * @see wp_get_connectors() 28 * @see _wp_connectors_init() 29 * 30 * @phpstan-type Connector array{ 31 * name: non-empty-string, 32 * description: string, 33 * logo_url?: non-empty-string, 34 * type: non-empty-string, 35 * authentication: array{ 36 * method: 'api_key'|'none', 37 * credentials_url?: non-empty-string, 38 * setting_name?: non-empty-string, 39 * constant_name?: non-empty-string, 40 * env_var_name?: non-empty-string 41 * }, 42 * plugin: array{ 43 * file?: non-empty-string, 44 * is_active: callable(): bool 45 * } 46 * } 47 */ 48 final class WP_Connector_Registry { 49 /** 50 * The singleton instance of the registry. 51 * 52 * @since 7.0.0 53 */ 54 private static ?WP_Connector_Registry $instance = null; 55 56 /** 57 * Holds the registered connectors. 58 * 59 * Each connector is stored as an associative array with keys: 60 * name, description, type, authentication, and optionally plugin. 61 * 62 * @since 7.0.0 63 * @var array<string, array> 64 * @phpstan-var array<string, Connector> 65 */ 66 private array $registered_connectors = array(); 67 68 /** 69 * Registers a new connector. 70 * 71 * Validates the provided arguments and stores the connector in the registry. 72 * For connectors with `api_key` authentication, a `setting_name` can be provided 73 * explicitly. If omitted, one is automatically generated using the pattern 74 * `connectors_{$type}_{$id}_api_key`, with hyphens in the type and ID normalized 75 * to underscores (e.g., connector type `spam_filtering` with ID `my_plugin` produces 76 * `connectors_spam_filtering_my_plugin_api_key`). This setting name is used for the 77 * Settings API registration and REST API exposure. 78 * 79 * Registering a connector with an ID that is already registered will trigger a 80 * `_doing_it_wrong()` notice and return `null`. To override an existing connector, 81 * call `unregister()` first. 82 * 83 * @since 7.0.0 84 * 85 * @see WP_Connector_Registry::unregister() 86 * 87 * @param string $id The unique connector identifier. Must match the pattern 88 * `/^[a-z0-9_-]+$/` (lowercase alphanumeric, hyphens, and underscores only). 89 * @param array $args { 90 * An associative array of arguments for the connector. 91 * 92 * @type string $name Required. The connector's display name. 93 * @type string $description Optional. The connector's description. Default empty string. 94 * @type string $logo_url Optional. URL to the connector's logo image. 95 * @type string $type Required. The connector type, e.g. 'ai_provider'. 96 * @type array $authentication { 97 * Required. Authentication configuration. 98 * 99 * @type string $method Required. The authentication method: 'api_key' or 'none'. 100 * @type string $credentials_url Optional. URL where users can obtain API credentials. 101 * @type string $setting_name Optional. The setting name for the API key. 102 * When omitted, auto-generated as 103 * `connectors_{$type}_{$id}_api_key`. 104 * Must be a non-empty string when provided. 105 * @type string $constant_name Optional. PHP constant name for the API key 106 * (e.g. 'ANTHROPIC_API_KEY'). Only checked when provided. 107 * @type string $env_var_name Optional. Environment variable name for the API key 108 * (e.g. 'ANTHROPIC_API_KEY'). Only checked when provided. 109 * } 110 * @type array $plugin { 111 * Optional. Plugin data for install/activate UI. 112 * 113 * @type string $file Optional. The plugin's main file path relative to the 114 * plugins directory (e.g. 'my-plugin/my-plugin.php' or 115 * 'hello.php'). 116 * @type callable $is_active Optional callback to determine whether the plugin 117 * is active. Receives no arguments and must return bool. 118 * Defaults to `__return_true`. 119 * } 120 * } 121 * @return array|null The registered connector data on success, null on failure. 122 * 123 * @phpstan-param array{ 124 * name: non-empty-string, 125 * description?: string, 126 * logo_url?: non-empty-string, 127 * type: non-empty-string, 128 * authentication: array{ 129 * method: 'api_key'|'none', 130 * credentials_url?: non-empty-string, 131 * setting_name?: non-empty-string, 132 * constant_name?: non-empty-string, 133 * env_var_name?: non-empty-string 134 * }, 135 * plugin?: array{ 136 * file?: non-empty-string, 137 * is_active?: callable(): bool 138 * } 139 * } $args 140 * @phpstan-return Connector|null 141 */ 142 public function register( string $id, array $args ): ?array { 143 if ( ! preg_match( '/^[a-z0-9_-]+$/', $id ) ) { 144 _doing_it_wrong( 145 __METHOD__, 146 __( 147 'Connector ID must contain only lowercase alphanumeric characters, hyphens, and underscores.' 148 ), 149 '7.0.0' 150 ); 151 return null; 152 } 153 154 if ( $this->is_registered( $id ) ) { 155 _doing_it_wrong( 156 __METHOD__, 157 /* translators: %s: Connector ID. */ 158 sprintf( __( 'Connector "%s" is already registered.' ), esc_html( $id ) ), 159 '7.0.0' 160 ); 161 return null; 162 } 163 164 // Validate required fields. 165 if ( empty( $args['name'] ) || ! is_string( $args['name'] ) ) { 166 _doing_it_wrong( 167 __METHOD__, 168 /* translators: %s: Connector ID. */ 169 sprintf( __( 'Connector "%s" requires a non-empty "name" string.' ), esc_html( $id ) ), 170 '7.0.0' 171 ); 172 return null; 173 } 174 175 if ( empty( $args['type'] ) || ! is_string( $args['type'] ) ) { 176 _doing_it_wrong( 177 __METHOD__, 178 /* translators: %s: Connector ID. */ 179 sprintf( __( 'Connector "%s" requires a non-empty "type" string.' ), esc_html( $id ) ), 180 '7.0.0' 181 ); 182 return null; 183 } 184 185 if ( ! isset( $args['authentication'] ) || ! is_array( $args['authentication'] ) ) { 186 _doing_it_wrong( 187 __METHOD__, 188 /* translators: %s: Connector ID. */ 189 sprintf( __( 'Connector "%s" requires an "authentication" array.' ), esc_html( $id ) ), 190 '7.0.0' 191 ); 192 return null; 193 } 194 195 if ( empty( $args['authentication']['method'] ) || ! in_array( $args['authentication']['method'], array( 'api_key', 'none' ), true ) ) { 196 _doing_it_wrong( 197 __METHOD__, 198 /* translators: %s: Connector ID. */ 199 sprintf( __( 'Connector "%s" authentication method must be "api_key" or "none".' ), esc_html( $id ) ), 200 '7.0.0' 201 ); 202 return null; 203 } 204 205 if ( 'ai_provider' === $args['type'] && ! wp_supports_ai() ) { 206 // No need for a `doing_it_wrong` as AI support is disabled intentionally. 207 return null; 208 } 209 210 $connector = array( 211 'name' => $args['name'], 212 'description' => isset( $args['description'] ) && is_string( $args['description'] ) ? $args['description'] : '', 213 'type' => $args['type'], 214 'authentication' => array( 215 'method' => $args['authentication']['method'], 216 ), 217 ); 218 219 if ( ! empty( $args['logo_url'] ) && is_string( $args['logo_url'] ) ) { 220 $connector['logo_url'] = $args['logo_url']; 221 } 222 223 if ( 'api_key' === $args['authentication']['method'] ) { 224 if ( ! empty( $args['authentication']['credentials_url'] ) && is_string( $args['authentication']['credentials_url'] ) ) { 225 $connector['authentication']['credentials_url'] = $args['authentication']['credentials_url']; 226 } 227 if ( isset( $args['authentication']['setting_name'] ) ) { 228 if ( ! is_string( $args['authentication']['setting_name'] ) || '' === $args['authentication']['setting_name'] ) { 229 _doing_it_wrong( 230 __METHOD__, 231 /* translators: %s: Connector ID. */ 232 sprintf( __( 'Connector "%s" authentication setting_name must be a non-empty string.' ), esc_html( $id ) ), 233 '7.0.0' 234 ); 235 return null; 236 } 237 $connector['authentication']['setting_name'] = $args['authentication']['setting_name']; 238 } else { 239 $connector['authentication']['setting_name'] = str_replace( '-', '_', "connectors_{$connector['type']}_{$id}_api_key" ); 240 } 241 if ( isset( $args['authentication']['constant_name'] ) ) { 242 if ( ! is_string( $args['authentication']['constant_name'] ) || '' === $args['authentication']['constant_name'] ) { 243 _doing_it_wrong( 244 __METHOD__, 245 /* translators: %s: Connector ID. */ 246 sprintf( __( 'Connector "%s" authentication constant_name must be a non-empty string.' ), esc_html( $id ) ), 247 '7.0.0' 248 ); 249 return null; 250 } 251 $connector['authentication']['constant_name'] = $args['authentication']['constant_name']; 252 } 253 if ( isset( $args['authentication']['env_var_name'] ) ) { 254 if ( ! is_string( $args['authentication']['env_var_name'] ) || '' === $args['authentication']['env_var_name'] ) { 255 _doing_it_wrong( 256 __METHOD__, 257 /* translators: %s: Connector ID. */ 258 sprintf( __( 'Connector "%s" authentication env_var_name must be a non-empty string.' ), esc_html( $id ) ), 259 '7.0.0' 260 ); 261 return null; 262 } 263 $connector['authentication']['env_var_name'] = $args['authentication']['env_var_name']; 264 } 265 } 266 267 $connector['plugin'] = array(); 268 269 if ( ! empty( $args['plugin'] ) && is_array( $args['plugin'] ) ) { 270 if ( ! empty( $args['plugin']['file'] ) ) { 271 $connector['plugin']['file'] = $args['plugin']['file']; 272 } 273 274 if ( isset( $args['plugin']['is_active'] ) ) { 275 if ( ! is_callable( $args['plugin']['is_active'] ) ) { 276 _doing_it_wrong( 277 __METHOD__, 278 /* translators: %s: Connector ID. */ 279 sprintf( __( 'Connector "%s" plugin is_active must be callable.' ), esc_html( $id ) ), 280 '7.0.0' 281 ); 282 return null; 283 } 284 285 $connector['plugin']['is_active'] = $args['plugin']['is_active']; 286 } 287 } 288 289 if ( ! isset( $connector['plugin']['is_active'] ) ) { 290 $connector['plugin']['is_active'] = '__return_true'; 291 } 292 293 $this->registered_connectors[ $id ] = $connector; 294 return $connector; 295 } 296 297 /** 298 * Unregisters a connector. 299 * 300 * Returns the connector data on success, which can be modified and passed 301 * back to `register()` to override a connector's metadata. 302 * 303 * Triggers a `_doing_it_wrong()` notice if the connector is not registered. 304 * Use `is_registered()` to check first when the connector may not exist. 305 * 306 * @since 7.0.0 307 * 308 * @see WP_Connector_Registry::register() 309 * @see WP_Connector_Registry::is_registered() 310 * 311 * @param string $id The connector identifier. 312 * @return array|null The unregistered connector data on success, null on failure. 313 * 314 * @phpstan-return Connector|null 315 */ 316 public function unregister( string $id ): ?array { 317 if ( ! $this->is_registered( $id ) ) { 318 _doing_it_wrong( 319 __METHOD__, 320 /* translators: %s: Connector ID. */ 321 sprintf( __( 'Connector "%s" not found.' ), esc_html( $id ) ), 322 '7.0.0' 323 ); 324 return null; 325 } 326 327 $unregistered = $this->registered_connectors[ $id ]; 328 unset( $this->registered_connectors[ $id ] ); 329 330 return $unregistered; 331 } 332 333 /** 334 * Retrieves the list of all registered connectors. 335 * 336 * Do not use this method directly. Instead, use the `wp_get_connectors()` function. 337 * 338 * @since 7.0.0 339 * 340 * @see wp_get_connectors() 341 * 342 * @return array Connector settings keyed by connector ID. 343 * 344 * @phpstan-return array<string, Connector> 345 */ 346 public function get_all_registered(): array { 347 return $this->registered_connectors; 348 } 349 350 /** 351 * Checks if a connector is registered. 352 * 353 * Do not use this method directly. Instead, use the `wp_is_connector_registered()` function. 354 * 355 * @since 7.0.0 356 * 357 * @see wp_is_connector_registered() 358 * 359 * @param string $id The connector identifier. 360 * @return bool True if the connector is registered, false otherwise. 361 */ 362 public function is_registered( string $id ): bool { 363 return isset( $this->registered_connectors[ $id ] ); 364 } 365 366 /** 367 * Retrieves a registered connector. 368 * 369 * Do not use this method directly. Instead, use the `wp_get_connector()` function. 370 * 371 * Triggers a `_doing_it_wrong()` notice if the connector is not registered. 372 * Use `is_registered()` to check first when the connector may not exist. 373 * 374 * @since 7.0.0 375 * 376 * @see wp_get_connector() 377 * 378 * @param string $id The connector identifier. 379 * @return array|null The registered connector data, or null if it is not registered. 380 * @phpstan-return Connector|null 381 */ 382 public function get_registered( string $id ): ?array { 383 if ( ! $this->is_registered( $id ) ) { 384 _doing_it_wrong( 385 __METHOD__, 386 /* translators: %s: Connector ID. */ 387 sprintf( __( 'Connector "%s" not found.' ), esc_html( $id ) ), 388 '7.0.0' 389 ); 390 return null; 391 } 392 return $this->registered_connectors[ $id ]; 393 } 394 395 /** 396 * Retrieves the main instance of the registry class. 397 * 398 * @since 7.0.0 399 * 400 * @return WP_Connector_Registry|null The main registry instance, or null if not yet initialized. 401 */ 402 public static function get_instance(): ?self { 403 return self::$instance; 404 } 405 406 /** 407 * Sets the main instance of the registry class. 408 * 409 * Called by `_wp_connectors_init()` during the `init` action. Must not be 410 * called outside of that context. 411 * 412 * @since 7.0.0 413 * @access private 414 * 415 * @see _wp_connectors_init() 416 * 417 * @param WP_Connector_Registry $registry The registry instance. 418 */ 419 public static function set_instance( WP_Connector_Registry $registry ): void { 420 if ( ! doing_action( 'init' ) ) { 421 _doing_it_wrong( 422 __METHOD__, 423 __( 'The connector registry instance must be set during the <code>init</code> action.' ), 424 '7.0.0' 425 ); 426 return; 427 } 428 429 self::$instance = $registry; 430 } 431 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Sat Jun 13 09:38:55 2026 | Cross-referenced by PHPXref |