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