| [ 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 lowercase-string&non-empty-string $id 131 * @phpstan-param array{ 132 * name: non-empty-string, 133 * description?: string, 134 * logo_url?: non-empty-string, 135 * type: non-empty-string, 136 * authentication: array{ 137 * method: 'api_key'|'application_password'|'none', 138 * credentials_url?: non-empty-string, 139 * setting_name?: non-empty-string, 140 * constant_name?: non-empty-string, 141 * env_var_name?: non-empty-string 142 * }, 143 * plugin?: array{ 144 * file?: non-empty-string, 145 * is_active?: callable(): bool 146 * } 147 * } $args 148 * @phpstan-return Connector|null 149 */ 150 public function register( string $id, array $args ): ?array { 151 if ( ! preg_match( '/^[a-z0-9_-]+$/', $id ) ) { 152 _doing_it_wrong( 153 __METHOD__, 154 __( 155 'Connector ID must contain only lowercase alphanumeric characters, hyphens, and underscores.' 156 ), 157 '7.0.0' 158 ); 159 return null; 160 } 161 162 if ( $this->is_registered( $id ) ) { 163 _doing_it_wrong( 164 __METHOD__, 165 /* translators: %s: Connector ID. */ 166 sprintf( __( 'Connector "%s" is already registered.' ), esc_html( $id ) ), 167 '7.0.0' 168 ); 169 return null; 170 } 171 172 // Validate required fields. 173 if ( empty( $args['name'] ) || ! is_string( $args['name'] ) ) { 174 _doing_it_wrong( 175 __METHOD__, 176 /* translators: %s: Connector ID. */ 177 sprintf( __( 'Connector "%s" requires a non-empty "name" string.' ), esc_html( $id ) ), 178 '7.0.0' 179 ); 180 return null; 181 } 182 183 if ( empty( $args['type'] ) || ! is_string( $args['type'] ) ) { 184 _doing_it_wrong( 185 __METHOD__, 186 /* translators: %s: Connector ID. */ 187 sprintf( __( 'Connector "%s" requires a non-empty "type" string.' ), esc_html( $id ) ), 188 '7.0.0' 189 ); 190 return null; 191 } 192 193 if ( ! isset( $args['authentication'] ) || ! is_array( $args['authentication'] ) ) { 194 _doing_it_wrong( 195 __METHOD__, 196 /* translators: %s: Connector ID. */ 197 sprintf( __( 'Connector "%s" requires an "authentication" array.' ), esc_html( $id ) ), 198 '7.0.0' 199 ); 200 return null; 201 } 202 203 if ( empty( $args['authentication']['method'] ) || ! in_array( $args['authentication']['method'], array( 'api_key', 'application_password', 'none' ), true ) ) { 204 _doing_it_wrong( 205 __METHOD__, 206 /* translators: %s: Connector ID. */ 207 sprintf( __( 'Connector "%s" authentication method must be "api_key", "application_password", or "none".' ), esc_html( $id ) ), 208 '7.0.0' 209 ); 210 return null; 211 } 212 213 if ( 'ai_provider' === $args['type'] && ! wp_supports_ai() ) { 214 // No need for a `doing_it_wrong` as AI support is disabled intentionally. 215 return null; 216 } 217 218 $connector = array( 219 'name' => $args['name'], 220 'description' => isset( $args['description'] ) && is_string( $args['description'] ) ? $args['description'] : '', 221 'type' => $args['type'], 222 'authentication' => array( 223 'method' => $args['authentication']['method'], 224 ), 225 ); 226 227 if ( ! empty( $args['logo_url'] ) && is_string( $args['logo_url'] ) ) { 228 $connector['logo_url'] = $args['logo_url']; 229 } 230 231 $requires_credentials = in_array( $args['authentication']['method'], array( 'api_key', 'application_password' ), true ); 232 233 if ( $requires_credentials ) { 234 if ( ! empty( $args['authentication']['credentials_url'] ) && is_string( $args['authentication']['credentials_url'] ) ) { 235 $connector['authentication']['credentials_url'] = $args['authentication']['credentials_url']; 236 } 237 238 if ( isset( $args['authentication']['setting_name'] ) ) { 239 if ( ! is_string( $args['authentication']['setting_name'] ) || '' === $args['authentication']['setting_name'] ) { 240 _doing_it_wrong( 241 __METHOD__, 242 /* translators: %s: Connector ID. */ 243 sprintf( __( 'Connector "%s" authentication setting_name must be a non-empty string.' ), esc_html( $id ) ), 244 '7.0.0' 245 ); 246 return null; 247 } 248 $connector['authentication']['setting_name'] = $args['authentication']['setting_name']; 249 } else { 250 $connector['authentication']['setting_name'] = str_replace( '-', '_', "connectors_{$connector['type']}_{$id}_{$args['authentication']['method']}" ); 251 } 252 253 if ( isset( $args['authentication']['constant_name'] ) ) { 254 if ( ! is_string( $args['authentication']['constant_name'] ) || '' === $args['authentication']['constant_name'] ) { 255 _doing_it_wrong( 256 __METHOD__, 257 /* translators: %s: Connector ID. */ 258 sprintf( __( 'Connector "%s" authentication constant_name must be a non-empty string.' ), esc_html( $id ) ), 259 '7.0.0' 260 ); 261 return null; 262 } 263 $connector['authentication']['constant_name'] = $args['authentication']['constant_name']; 264 } 265 if ( isset( $args['authentication']['env_var_name'] ) ) { 266 if ( ! is_string( $args['authentication']['env_var_name'] ) || '' === $args['authentication']['env_var_name'] ) { 267 _doing_it_wrong( 268 __METHOD__, 269 /* translators: %s: Connector ID. */ 270 sprintf( __( 'Connector "%s" authentication env_var_name must be a non-empty string.' ), esc_html( $id ) ), 271 '7.0.0' 272 ); 273 return null; 274 } 275 $connector['authentication']['env_var_name'] = $args['authentication']['env_var_name']; 276 } 277 } 278 279 $connector['plugin'] = array(); 280 281 if ( ! empty( $args['plugin'] ) && is_array( $args['plugin'] ) ) { 282 if ( ! empty( $args['plugin']['file'] ) ) { 283 $connector['plugin']['file'] = $args['plugin']['file']; 284 } 285 286 if ( isset( $args['plugin']['is_active'] ) ) { 287 if ( ! is_callable( $args['plugin']['is_active'] ) ) { 288 _doing_it_wrong( 289 __METHOD__, 290 /* translators: %s: Connector ID. */ 291 sprintf( __( 'Connector "%s" plugin is_active must be callable.' ), esc_html( $id ) ), 292 '7.0.0' 293 ); 294 return null; 295 } 296 297 $connector['plugin']['is_active'] = $args['plugin']['is_active']; 298 } 299 } 300 301 if ( ! isset( $connector['plugin']['is_active'] ) ) { 302 $connector['plugin']['is_active'] = '__return_true'; 303 } 304 305 $this->registered_connectors[ $id ] = $connector; 306 return $connector; 307 } 308 309 /** 310 * Unregisters a connector. 311 * 312 * Returns the connector data on success, which can be modified and passed 313 * back to `register()` to override a connector's metadata. 314 * 315 * Triggers a `_doing_it_wrong()` notice if the connector is not registered. 316 * Use `is_registered()` to check first when the connector may not exist. 317 * 318 * @since 7.0.0 319 * 320 * @see WP_Connector_Registry::register() 321 * @see WP_Connector_Registry::is_registered() 322 * 323 * @param string $id The connector identifier. 324 * @return array|null The unregistered connector data on success, null on failure. 325 * 326 * @phpstan-return Connector|null 327 */ 328 public function unregister( string $id ): ?array { 329 if ( ! $this->is_registered( $id ) ) { 330 _doing_it_wrong( 331 __METHOD__, 332 /* translators: %s: Connector ID. */ 333 sprintf( __( 'Connector "%s" not found.' ), esc_html( $id ) ), 334 '7.0.0' 335 ); 336 return null; 337 } 338 339 $unregistered = $this->registered_connectors[ $id ]; 340 unset( $this->registered_connectors[ $id ] ); 341 342 return $unregistered; 343 } 344 345 /** 346 * Retrieves the list of all registered connectors. 347 * 348 * Do not use this method directly. Instead, use the `wp_get_connectors()` function. 349 * 350 * @since 7.0.0 351 * 352 * @see wp_get_connectors() 353 * 354 * @return array Connector settings keyed by connector ID. 355 * 356 * @phpstan-return array<string, Connector> 357 */ 358 public function get_all_registered(): array { 359 return $this->registered_connectors; 360 } 361 362 /** 363 * Checks if a connector is registered. 364 * 365 * Do not use this method directly. Instead, use the `wp_is_connector_registered()` function. 366 * 367 * @since 7.0.0 368 * 369 * @see wp_is_connector_registered() 370 * 371 * @param string $id The connector identifier. 372 * @return bool True if the connector is registered, false otherwise. 373 */ 374 public function is_registered( string $id ): bool { 375 return isset( $this->registered_connectors[ $id ] ); 376 } 377 378 /** 379 * Retrieves a registered connector. 380 * 381 * Do not use this method directly. Instead, use the `wp_get_connector()` function. 382 * 383 * Triggers a `_doing_it_wrong()` notice if the connector is not registered. 384 * Use `is_registered()` to check first when the connector may not exist. 385 * 386 * @since 7.0.0 387 * 388 * @see wp_get_connector() 389 * 390 * @param string $id The connector identifier. 391 * @return array|null The registered connector data, or null if it is not registered. 392 * @phpstan-return Connector|null 393 */ 394 public function get_registered( string $id ): ?array { 395 if ( ! $this->is_registered( $id ) ) { 396 _doing_it_wrong( 397 __METHOD__, 398 /* translators: %s: Connector ID. */ 399 sprintf( __( 'Connector "%s" not found.' ), esc_html( $id ) ), 400 '7.0.0' 401 ); 402 return null; 403 } 404 return $this->registered_connectors[ $id ]; 405 } 406 407 /** 408 * Retrieves the main instance of the registry class. 409 * 410 * @since 7.0.0 411 * 412 * @return WP_Connector_Registry|null The main registry instance, or null if not yet initialized. 413 */ 414 public static function get_instance(): ?self { 415 return self::$instance; 416 } 417 418 /** 419 * Sets the main instance of the registry class. 420 * 421 * Called by `_wp_connectors_init()` during the `init` action. Must not be 422 * called outside of that context. 423 * 424 * @since 7.0.0 425 * @access private 426 * 427 * @see _wp_connectors_init() 428 * 429 * @param WP_Connector_Registry $registry The registry instance. 430 */ 431 public static function set_instance( WP_Connector_Registry $registry ): void { 432 if ( ! doing_action( 'init' ) ) { 433 _doing_it_wrong( 434 __METHOD__, 435 __( 'The connector registry instance must be set during the <code>init</code> action.' ), 436 '7.0.0' 437 ); 438 return; 439 } 440 441 self::$instance = $registry; 442 } 443 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Wed Oct 14 08:20:33 2026 | Cross-referenced by PHPXref |