Skip to content

Commit 0b33818

Browse files
committed
Connectors: Refine plugin install check and PHPStan types.
Replace `file_exists()` with `validate_plugin()` in the connectors script module data, and refine PHPStan type definitions in the connectors code to better reflect the actual shape of registered connector data. Follow-up to [62288]. Props jorgefilipecosta, mukesh27, peterwilsoncc, westonruter, wildworks. See #65020. git-svn-id: https://develop.svn.wordpress.org/trunk@62332 602fd350-edb4-49c9-b593-d223f7449a82
1 parent a50e061 commit 0b33818

2 files changed

Lines changed: 67 additions & 46 deletions

File tree

src/wp-includes/class-wp-connector-registry.php

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
*
3030
* @phpstan-type Connector array{
3131
* name: non-empty-string,
32-
* description: non-empty-string,
32+
* description: string,
3333
* logo_url?: non-empty-string,
3434
* type: non-empty-string,
3535
* authentication: array{
@@ -39,9 +39,9 @@
3939
* constant_name?: non-empty-string,
4040
* env_var_name?: non-empty-string
4141
* },
42-
* plugin?: array{
43-
* file: non-empty-string,
44-
* is_active?: callable(): bool
42+
* plugin: array{
43+
* file?: non-empty-string,
44+
* is_active: callable(): bool
4545
* }
4646
* }
4747
*/
@@ -120,7 +120,23 @@ final class WP_Connector_Registry {
120120
* }
121121
* @return array|null The registered connector data on success, null on failure.
122122
*
123-
* @phpstan-param Connector $args
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
124140
* @phpstan-return Connector|null
125141
*/
126142
public function register( string $id, array $args ): ?array {

src/wp-includes/connectors.php

Lines changed: 46 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ function wp_is_connector_registered( string $id ): bool {
5858
* @type array $plugin {
5959
* Optional. Plugin data for install/activate UI.
6060
*
61-
* @type string $file The plugin's main file path relative to the plugins
62-
* directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php').
61+
* @type string $file The plugin's main file path relative to the plugins
62+
* directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php').
63+
* @type callable $is_active Callback to determine whether the plugin is active. Receives no arguments and must return bool.
64+
* Defaults to `__return_true`.
6365
* }
6466
* }
6567
* @phpstan-return ?array{
6668
* name: non-empty-string,
67-
* description: non-empty-string,
69+
* description: string,
6870
* logo_url?: non-empty-string,
6971
* type: non-empty-string,
7072
* authentication: array{
@@ -74,8 +76,9 @@ function wp_is_connector_registered( string $id ): bool {
7476
* constant_name?: non-empty-string,
7577
* env_var_name?: non-empty-string
7678
* },
77-
* plugin?: array{
78-
* file: non-empty-string
79+
* plugin: array{
80+
* file?: non-empty-string,
81+
* is_active: callable(): bool,
7982
* }
8083
* }
8184
*/
@@ -119,14 +122,16 @@ function wp_get_connector( string $id ): ?array {
119122
* @type array $plugin {
120123
* Optional. Plugin data for install/activate UI.
121124
*
122-
* @type string $file The plugin's main file path relative to the plugins
123-
* directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php').
125+
* @type string $file The plugin's main file path relative to the plugins
126+
* directory (e.g. 'my-plugin/my-plugin.php' or 'hello.php').
127+
* @type callable $is_active Callback to determine whether the plugin is active. Receives no arguments and must return bool.
128+
* Defaults to `__return_true`.
124129
* }
125130
* }
126131
* }
127132
* @phpstan-return array<string, array{
128133
* name: non-empty-string,
129-
* description: non-empty-string,
134+
* description: string,
130135
* logo_url?: non-empty-string,
131136
* type: non-empty-string,
132137
* authentication: array{
@@ -136,8 +141,9 @@ function wp_get_connector( string $id ): ?array {
136141
* constant_name?: non-empty-string,
137142
* env_var_name?: non-empty-string
138143
* },
139-
* plugin?: array{
140-
* file: non-empty-string
144+
* plugin: array{
145+
* file?: non-empty-string,
146+
* is_active: callable(): bool,
141147
* }
142148
* }>
143149
*/
@@ -160,7 +166,7 @@ function wp_get_connectors(): array {
160166
* @access private
161167
*
162168
* @param string $path Absolute path to the logo file.
163-
* @return string|null The URL to the logo file, or null if the path is invalid.
169+
* @return non-empty-string|null The URL to the logo file, or null if the path is invalid.
164170
*/
165171
function _wp_connectors_resolve_ai_provider_logo_url( string $path ): ?string {
166172
if ( ! $path ) {
@@ -175,12 +181,14 @@ function _wp_connectors_resolve_ai_provider_logo_url( string $path ): ?string {
175181

176182
$mu_plugin_dir = wp_normalize_path( WPMU_PLUGIN_DIR );
177183
if ( str_starts_with( $path, $mu_plugin_dir . '/' ) ) {
178-
return plugins_url( substr( $path, strlen( $mu_plugin_dir ) ), WPMU_PLUGIN_DIR . '/.' );
184+
$logo_url = plugins_url( substr( $path, strlen( $mu_plugin_dir ) ), WPMU_PLUGIN_DIR . '/.' );
185+
return $logo_url ? $logo_url : null;
179186
}
180187

181188
$plugin_dir = wp_normalize_path( WP_PLUGIN_DIR );
182189
if ( str_starts_with( $path, $plugin_dir . '/' ) ) {
183-
return plugins_url( substr( $path, strlen( $plugin_dir ) ) );
190+
$logo_url = plugins_url( substr( $path, strlen( $plugin_dir ) ) );
191+
return $logo_url ? $logo_url : null;
184192
}
185193

186194
_doing_it_wrong(
@@ -317,7 +325,7 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
317325
// Registry values (from provider plugins) take precedence over hardcoded fallbacks.
318326
$ai_registry = AiClient::defaultRegistry();
319327

320-
foreach ( $ai_registry->getRegisteredProviderIds() as $connector_id ) {
328+
foreach ( array_filter( $ai_registry->getRegisteredProviderIds() ) as $connector_id ) {
321329
$provider_class_name = $ai_registry->getProviderClassName( $connector_id );
322330
$provider_metadata = $provider_class_name::metadata();
323331

@@ -327,9 +335,11 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
327335
if ( $is_api_key ) {
328336
$credentials_url = $provider_metadata->getCredentialsUrl();
329337
$authentication = array(
330-
'method' => 'api_key',
331-
'credentials_url' => $credentials_url ? $credentials_url : null,
338+
'method' => 'api_key',
332339
);
340+
if ( $credentials_url ) {
341+
$authentication['credentials_url'] = $credentials_url;
342+
}
333343
} else {
334344
$authentication = array( 'method' => 'none' );
335345
}
@@ -362,8 +372,10 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
362372
'description' => $description ? $description : '',
363373
'type' => 'ai_provider',
364374
'authentication' => $authentication,
365-
'logo_url' => $logo_url,
366375
);
376+
if ( $logo_url ) {
377+
$defaults[ $connector_id ]['logo_url'] = $logo_url;
378+
}
367379
}
368380
}
369381

@@ -372,33 +384,22 @@ function _wp_connectors_register_default_ai_providers( WP_Connector_Registry $re
372384
if ( 'api_key' === $args['authentication']['method'] ) {
373385
$sanitized_id = str_replace( '-', '_', $id );
374386

375-
if ( ! isset( $args['authentication']['setting_name'] ) ) {
376-
$args['authentication']['setting_name'] = "connectors_ai_{$sanitized_id}_api_key";
377-
}
387+
$args['authentication']['setting_name'] = "connectors_ai_{$sanitized_id}_api_key";
378388

379389
// All AI providers use the {CONSTANT_CASE_ID}_API_KEY naming convention.
380-
if ( ! isset( $args['authentication']['constant_name'] ) || ! isset( $args['authentication']['env_var_name'] ) ) {
381-
$constant_case_key = strtoupper( preg_replace( '/([a-z])([A-Z])/', '$1_$2', $sanitized_id ) ) . '_API_KEY';
382-
383-
if ( ! isset( $args['authentication']['constant_name'] ) ) {
384-
$args['authentication']['constant_name'] = $constant_case_key;
385-
}
390+
$constant_case_key = strtoupper( (string) preg_replace( '/([a-z])([A-Z])/', '$1_$2', $sanitized_id ) ) . '_API_KEY';
386391

387-
if ( ! isset( $args['authentication']['env_var_name'] ) ) {
388-
$args['authentication']['env_var_name'] = $constant_case_key;
389-
}
390-
}
392+
$args['authentication']['constant_name'] = $constant_case_key;
393+
$args['authentication']['env_var_name'] = $constant_case_key;
391394
}
392395

393-
if ( ! isset( $args['plugin']['is_active'] ) ) {
394-
$args['plugin']['is_active'] = static function () use ( $ai_registry, $id ): bool {
395-
try {
396-
return $ai_registry->hasProvider( $id );
397-
} catch ( Exception $e ) {
398-
return false;
399-
}
400-
};
401-
}
396+
$args['plugin']['is_active'] = static function () use ( $ai_registry, $id ): bool {
397+
try {
398+
return $ai_registry->hasProvider( $id );
399+
} catch ( Exception $e ) {
400+
return false;
401+
}
402+
};
402403

403404
$registry->register( $id, $args );
404405
}
@@ -646,7 +647,7 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void {
646647
}
647648

648649
$api_key = get_option( $auth['setting_name'], '' );
649-
if ( '' === $api_key ) {
650+
if ( ! is_string( $api_key ) || '' === $api_key ) {
650651
continue;
651652
}
652653

@@ -673,6 +674,10 @@ function _wp_connectors_pass_default_keys_to_ai_client(): void {
673674
function _wp_connectors_get_connector_script_module_data( array $data ): array {
674675
$registry = AiClient::defaultRegistry();
675676

677+
if ( ! function_exists( 'validate_plugin' ) ) {
678+
require_once ABSPATH . 'wp-admin/includes/plugin.php';
679+
}
680+
676681
$connectors = array();
677682
foreach ( wp_get_connectors() as $connector_id => $connector_data ) {
678683
$auth = $connector_data['authentication'];
@@ -706,7 +711,7 @@ function _wp_connectors_get_connector_script_module_data( array $data ): array {
706711
if ( ! empty( $connector_data['plugin']['file'] ) ) {
707712
$file = $connector_data['plugin']['file'];
708713
$is_activated = (bool) call_user_func( $connector_data['plugin']['is_active'] );
709-
$is_installed = $is_activated || file_exists( wp_normalize_path( WP_PLUGIN_DIR . '/' . $file ) );
714+
$is_installed = $is_activated || 0 === validate_plugin( $file );
710715

711716
$connector_out['plugin'] = array(
712717
'file' => $file,

0 commit comments

Comments
 (0)