- get_model_preference_from_request();
- $user_id = get_current_user_id();
-
- if ( empty( $input ) ) {
- $this->handle_analyze_error( $user_id, '', new WP_Error( 'missing_input', __( 'Please enter a plugin name.', 'plugin-check' ) ) );
- return;
- }
-
- $ai_config = $this->get_ai_config( $model_preference );
- if ( is_wp_error( $ai_config ) ) {
- $this->handle_analyze_error( $user_id, $input, $ai_config );
- return;
- }
-
- $analysis = $this->run_name_analysis( $ai_config['model_preference'], $input, $author );
-
- if ( is_wp_error( $analysis ) ) {
- $this->handle_analyze_error( $user_id, $input, $analysis );
- return;
- }
-
- $this->store_result(
- $user_id,
- array(
- 'input' => $input,
- 'analysis' => $analysis,
- )
- );
- wp_safe_redirect( $this->get_page_url() );
- exit;
- }
-
- /**
- * Handles analyze error and redirects.
- *
- * @since 1.8.0
- *
- * @param int $user_id User ID.
- * @param string $input Input value.
- * @param WP_Error $error Error object.
- */
- protected function handle_analyze_error( $user_id, $input, $error ) {
- $this->store_result(
- $user_id,
- array(
- 'input' => $input,
- 'error' => $error,
- )
- );
- wp_safe_redirect( $this->get_page_url() );
- exit;
- }
-
- /**
- * Gets the page URL.
- *
- * @since 1.8.0
- *
- * @return string
- */
- protected function get_page_url() {
- return add_query_arg( array( 'page' => self::MENU_SLUG ), admin_url( 'tools.php' ) );
- }
-}
diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php
index 8f66010ac..5040bec8b 100644
--- a/includes/Checker/Abstract_Check_Runner.php
+++ b/includes/Checker/Abstract_Check_Runner.php
@@ -342,11 +342,22 @@ final public function set_ai_model_preference( $model_preference ) {
*
* @return bool True if AI analysis should be used, false otherwise.
*/
- protected function should_use_ai() {
+ public function should_use_ai() {
// Check if explicitly set via setter (e.g., CLI flag or checkbox).
return $this->use_ai;
}
+ /**
+ * Gets the AI model preference for analysis.
+ *
+ * @since 2.1.0
+ *
+ * @return string Model preference.
+ */
+ final public function get_ai_model_preference() {
+ return $this->ai_model_preference;
+ }
+
/**
* Sets categories for filtering the checks.
*
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
new file mode 100644
index 000000000..5e5a4d109
--- /dev/null
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -0,0 +1,179 @@
+should_use_ai() ) {
+ return;
+ }
+
+ if ( is_wp_error( $this->check_ai_prerequisites() ) || is_wp_error( $this->check_ai_connectors() ) ) {
+ return;
+ }
+
+ // Retrieve model preference.
+ $model_preference = '';
+ if ( method_exists( $runner, 'get_ai_model_preference' ) ) {
+ $model_preference = $runner->get_ai_model_preference();
+ }
+ if ( empty( $model_preference ) && class_exists( Settings_Page::class ) ) {
+ $model_preference = Settings_Page::get_model_preference();
+ }
+
+ if ( ! function_exists( 'get_plugin_data' ) ) {
+ require_once ABSPATH . 'wp-admin/includes/plugin.php';
+ }
+
+ $plugin_main_file = $result->plugin()->main_file();
+ $plugin_header = get_plugin_data( $plugin_main_file );
+ $plugin_name = isset( $plugin_header['Name'] ) ? $plugin_header['Name'] : '';
+ $author_name = isset( $plugin_header['AuthorName'] ) ? $plugin_header['AuthorName'] : '';
+
+ if ( empty( $plugin_name ) ) {
+ return;
+ }
+
+ $analysis = $this->run_name_analysis( $model_preference, $plugin_name, $author_name );
+ if ( is_wp_error( $analysis ) ) {
+ $this->add_result_warning_for_file(
+ $result,
+ sprintf(
+ /* translators: %s: Error message. */
+ __( 'AI plugin name check failed: %s', 'plugin-check' ),
+ $analysis->get_error_message()
+ ),
+ 'ai_name_check_failed',
+ $plugin_main_file
+ );
+ return;
+ }
+
+ $parsed = $this->parse_analysis( $analysis );
+
+ // If there is a verdict indicating issues:
+ if ( isset( $parsed['processed_data'] ) ) {
+ $data = $parsed['processed_data'];
+
+ // 1. If disallowed:
+ if ( ! empty( $data['disallowed'] ) ) {
+ $msg = isset( $data['disallowed_explanation'] ) ? $data['disallowed_explanation'] : __( 'The plugin name is disallowed.', 'plugin-check' );
+ $this->add_result_error_for_file(
+ $result,
+ $msg,
+ 'plugin_name_disallowed',
+ $plugin_main_file
+ );
+ }
+
+ // 2. If possible naming issues:
+ if ( ! empty( $data['possible_naming_issues'] ) ) {
+ $msg = isset( $data['naming_explanation'] ) ? $data['naming_explanation'] : __( 'The plugin name has possible naming issues.', 'plugin-check' );
+ $this->add_result_warning_for_file(
+ $result,
+ $msg,
+ 'plugin_name_issue',
+ $plugin_main_file
+ );
+ }
+
+ // 3. If possible owner/trademark issues:
+ if ( ! empty( $data['possible_owner_issues'] ) ) {
+ $msg = isset( $data['owner_explanation'] ) ? $data['owner_explanation'] : __( 'The plugin name has possible trademark or ownership issues.', 'plugin-check' );
+ $this->add_result_warning_for_file(
+ $result,
+ $msg,
+ 'plugin_name_trademark_issue',
+ $plugin_main_file
+ );
+ }
+
+ // 4. Group list of similar plugins/trademarks into recommendations:
+ if ( ! empty( $parsed['confusion_existing_plugins'] ) && is_array( $parsed['confusion_existing_plugins'] ) ) {
+ $plugins_list = array();
+ foreach ( $parsed['confusion_existing_plugins'] as $plugin ) {
+ $plugins_list[] = sprintf( '%s (%s, %s active installs)', $plugin['name'], $plugin['similarity_level'], $plugin['active_installations'] );
+ }
+ $this->add_result_warning_for_file(
+ $result,
+ sprintf(
+ /* translators: %s: List of similar plugins. */
+ __( 'Plugin name is similar to existing plugins: %s', 'plugin-check' ),
+ implode( '; ', $plugins_list )
+ ),
+ 'plugin_name_similarity',
+ $plugin_main_file
+ );
+ }
+ }
+ }
+
+ /**
+ * Gets the description for the check.
+ *
+ * @since 2.1.0
+ *
+ * @return string Description.
+ */
+ public function get_description(): string {
+ return __( 'Checks the plugin name for guideline compliance, trademark conflicts, and similarity with existing plugins using AI.', 'plugin-check' );
+ }
+
+ /**
+ * Gets the documentation URL for the check.
+ *
+ * @since 2.1.0
+ *
+ * @return string The documentation URL.
+ */
+ public function get_documentation_url(): string {
+ return __( 'https://developer.wordpress.org/plugins/wordpress-org/detailed-plugin-guidelines/', 'plugin-check' );
+ }
+}
diff --git a/includes/Checker/Default_Check_Repository.php b/includes/Checker/Default_Check_Repository.php
index d4d5c548d..00778a080 100644
--- a/includes/Checker/Default_Check_Repository.php
+++ b/includes/Checker/Default_Check_Repository.php
@@ -105,6 +105,7 @@ private function register_default_checks() {
'external_admin_menu_links' => new Checks\Plugin_Repo\External_Admin_Menu_Links_Check(),
'wp_functions_compatibility' => new Checks\Plugin_Repo\WP_Functions_Compatibility_Check(),
'ai_provider' => new Checks\General\AI_Provider_Check(),
+ 'ai_name' => new Checks\Plugin_Repo\AI_Name_Check(),
)
);
diff --git a/includes/Plugin_Main.php b/includes/Plugin_Main.php
index 210034611..cfe422fc3 100644
--- a/includes/Plugin_Main.php
+++ b/includes/Plugin_Main.php
@@ -78,14 +78,12 @@ public function add_hooks() {
// Create the Settings page.
$settings_page = new Settings_Page();
$settings_page->add_hooks();
-
- // Create the Plugin Check Namer tool page.
- $namer_page_class = '\\WordPress\\Plugin_Check\\Admin\\Namer_Page';
- $namer_page = new $namer_page_class();
- $namer_page->add_hooks();
+<<<<<<< HEAD
// Create the SVN Checker page.
$svn_checker_page = new SVN_Checker_Page();
$svn_checker_page->add_hooks();
+=======
+>>>>>>> 443d8a22 (feat: integrate Plugin Check Namer as standard AI Name Check static check)
}
}
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
new file mode 100644
index 000000000..3bc47a50c
--- /dev/null
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -0,0 +1,33 @@
+createMock( AJAX_Runner::class );
+ $runner->method( 'should_use_ai' )->willReturn( false );
+
+ $reflection = new \ReflectionClass( Plugin_Request_Utility::class );
+ $property = $reflection->getProperty( 'runner' );
+ $property->setAccessible( true );
+ $property->setValue( null, $runner );
+
+ $check = new AI_Name_Check();
+ $context = $this->createMock( Check_Context::class );
+ $result = new Check_Result( $context );
+
+ $check->run( $result );
+
+ $this->assertEmpty( $result->get_errors() );
+ $this->assertEmpty( $result->get_warnings() );
+
+ $property->setValue( null, null );
+ }
+}
From e002a1c5ebde3f23c911c609cfa9527e08a90720 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Mon, 13 Jul 2026 12:24:36 +0530
Subject: [PATCH 02/10] fix: resolve PHPCS formatting
---
includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php | 8 ++++----
.../phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php | 4 ++--
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index 5e5a4d109..aba4a0d27 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -7,15 +7,15 @@
namespace WordPress\Plugin_Check\Checker\Checks\Plugin_Repo;
+use WordPress\Plugin_Check\Admin\Settings_Page;
use WordPress\Plugin_Check\Checker\Check_Categories;
use WordPress\Plugin_Check\Checker\Check_Result;
use WordPress\Plugin_Check\Checker\Static_Check;
-use WordPress\Plugin_Check\Traits\Amend_Check_Result;
-use WordPress\Plugin_Check\Traits\Stable_Check;
use WordPress\Plugin_Check\Traits\AI_Check_Names;
use WordPress\Plugin_Check\Traits\AI_Utils;
+use WordPress\Plugin_Check\Traits\Amend_Check_Result;
+use WordPress\Plugin_Check\Traits\Stable_Check;
use WordPress\Plugin_Check\Utilities\Plugin_Request_Utility;
-use WordPress\Plugin_Check\Admin\Settings_Page;
/**
* Check for plugin name issues using AI.
@@ -98,7 +98,7 @@ public function run( Check_Result $result ) {
$parsed = $this->parse_analysis( $analysis );
- // If there is a verdict indicating issues:
+ // If there is a verdict indicating issues.
if ( isset( $parsed['processed_data'] ) ) {
$data = $parsed['processed_data'];
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
index 3bc47a50c..54864aa55 100644
--- a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -1,12 +1,12 @@
-
Date: Mon, 13 Jul 2026 12:30:39 +0530
Subject: [PATCH 03/10] fix: resolve sorting
---
.../phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
index 54864aa55..759cd6d1f 100644
--- a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -1,11 +1,10 @@
- setValue( null, $runner );
$check = new AI_Name_Check();
- $context = $this->createMock( Check_Context::class );
+ $context = new Check_Context( WP_PLUGIN_CHECK_MAIN_FILE );
$result = new Check_Result( $context );
$check->run( $result );
From 589006b82c6e2ae54f934b4b5fb0e4bc96a681be Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Mon, 13 Jul 2026 12:37:41 +0530
Subject: [PATCH 04/10] refactor: reduce method length
---
.../Checks/Plugin_Repo/AI_Name_Check.php | 143 ++++++++++++------
1 file changed, 93 insertions(+), 50 deletions(-)
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index aba4a0d27..30c5bde6c 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -97,61 +97,104 @@ public function run( Check_Result $result ) {
}
$parsed = $this->parse_analysis( $analysis );
+ $this->process_analysis_results( $result, $parsed, $plugin_main_file );
+ }
- // If there is a verdict indicating issues.
- if ( isset( $parsed['processed_data'] ) ) {
- $data = $parsed['processed_data'];
-
- // 1. If disallowed:
- if ( ! empty( $data['disallowed'] ) ) {
- $msg = isset( $data['disallowed_explanation'] ) ? $data['disallowed_explanation'] : __( 'The plugin name is disallowed.', 'plugin-check' );
- $this->add_result_error_for_file(
- $result,
- $msg,
- 'plugin_name_disallowed',
- $plugin_main_file
- );
- }
+ /**
+ * Process analysis results and add appropriate warnings/errors.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $parsed The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function process_analysis_results( Check_Result $result, array $parsed, string $plugin_main_file ) {
+ if ( ! isset( $parsed['processed_data'] ) ) {
+ return;
+ }
- // 2. If possible naming issues:
- if ( ! empty( $data['possible_naming_issues'] ) ) {
- $msg = isset( $data['naming_explanation'] ) ? $data['naming_explanation'] : __( 'The plugin name has possible naming issues.', 'plugin-check' );
- $this->add_result_warning_for_file(
- $result,
- $msg,
- 'plugin_name_issue',
- $plugin_main_file
- );
- }
+ $data = $parsed['processed_data'];
- // 3. If possible owner/trademark issues:
- if ( ! empty( $data['possible_owner_issues'] ) ) {
- $msg = isset( $data['owner_explanation'] ) ? $data['owner_explanation'] : __( 'The plugin name has possible trademark or ownership issues.', 'plugin-check' );
- $this->add_result_warning_for_file(
- $result,
- $msg,
- 'plugin_name_trademark_issue',
- $plugin_main_file
- );
- }
+ $this->check_disallowed_name( $result, $data, $plugin_main_file );
+ $this->check_naming_issues( $result, $data, $plugin_main_file );
+ $this->check_owner_issues( $result, $data, $plugin_main_file );
+ $this->check_similar_plugins( $result, $parsed, $plugin_main_file );
+ }
- // 4. Group list of similar plugins/trademarks into recommendations:
- if ( ! empty( $parsed['confusion_existing_plugins'] ) && is_array( $parsed['confusion_existing_plugins'] ) ) {
- $plugins_list = array();
- foreach ( $parsed['confusion_existing_plugins'] as $plugin ) {
- $plugins_list[] = sprintf( '%s (%s, %s active installs)', $plugin['name'], $plugin['similarity_level'], $plugin['active_installations'] );
- }
- $this->add_result_warning_for_file(
- $result,
- sprintf(
- /* translators: %s: List of similar plugins. */
- __( 'Plugin name is similar to existing plugins: %s', 'plugin-check' ),
- implode( '; ', $plugins_list )
- ),
- 'plugin_name_similarity',
- $plugin_main_file
- );
+ /**
+ * Check if the plugin name is disallowed.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $data The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_disallowed_name( Check_Result $result, array $data, string $plugin_main_file ) {
+ if ( ! empty( $data['disallowed'] ) ) {
+ $msg = isset( $data['disallowed_explanation'] ) ? $data['disallowed_explanation'] : __( 'The plugin name is disallowed.', 'plugin-check' );
+ $this->add_result_error_for_file( $result, $msg, 'plugin_name_disallowed', $plugin_main_file );
+ }
+ }
+
+ /**
+ * Check for possible naming issues.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $data The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_naming_issues( Check_Result $result, array $data, string $plugin_main_file ) {
+ if ( ! empty( $data['possible_naming_issues'] ) ) {
+ $msg = isset( $data['naming_explanation'] ) ? $data['naming_explanation'] : __( 'The plugin name has possible naming issues.', 'plugin-check' );
+ $this->add_result_warning_for_file( $result, $msg, 'plugin_name_issue', $plugin_main_file );
+ }
+ }
+
+ /**
+ * Check for possible owner issues.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $data The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_owner_issues( Check_Result $result, array $data, string $plugin_main_file ) {
+ if ( ! empty( $data['possible_owner_issues'] ) ) {
+ $msg = isset( $data['owner_explanation'] ) ? $data['owner_explanation'] : __( 'The plugin name has possible trademark or ownership issues.', 'plugin-check' );
+ $this->add_result_warning_for_file( $result, $msg, 'plugin_name_trademark_issue', $plugin_main_file );
+ }
+ }
+
+ /**
+ * Check for similar plugins.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param array $parsed The parsed analysis data.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function check_similar_plugins( Check_Result $result, array $parsed, string $plugin_main_file ) {
+ if ( ! empty( $parsed['confusion_existing_plugins'] ) && is_array( $parsed['confusion_existing_plugins'] ) ) {
+ $plugins_list = array();
+ foreach ( $parsed['confusion_existing_plugins'] as $plugin ) {
+ $plugins_list[] = sprintf( '%s (%s, %s active installs)', $plugin['name'], $plugin['similarity_level'], $plugin['active_installations'] );
}
+ $this->add_result_warning_for_file(
+ $result,
+ sprintf(
+ /* translators: %s: List of similar plugins. */
+ __( 'Plugin name is similar to existing plugins: %s', 'plugin-check' ),
+ implode( '; ', $plugins_list )
+ ),
+ 'plugin_name_similarity',
+ $plugin_main_file
+ );
}
}
From a3d94069690d11a649e7ed83776242f28fde2bf5 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Mon, 13 Jul 2026 12:41:50 +0530
Subject: [PATCH 05/10] refactor: reduce run method NPath complexity to satisfy
PHPMD
---
.../Checks/Plugin_Repo/AI_Name_Check.php | 100 +++++++++++++++---
1 file changed, 83 insertions(+), 17 deletions(-)
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index 30c5bde6c..384fe68cf 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -49,39 +49,105 @@ public function get_categories() {
*/
public function run( Check_Result $result ) {
$runner = Plugin_Request_Utility::get_runner();
-
- // Only run when AI analysis is enabled.
- if ( ! $runner || ! method_exists( $runner, 'should_use_ai' ) || ! $runner->should_use_ai() ) {
+ if ( ! $this->should_run_ai_check( $runner ) ) {
return;
}
- if ( is_wp_error( $this->check_ai_prerequisites() ) || is_wp_error( $this->check_ai_connectors() ) ) {
+ $model_preference = $this->get_model_preference( $runner );
+ $plugin_data = $this->get_plugin_name_and_author( $result );
+ if ( ! $plugin_data ) {
return;
}
- // Retrieve model preference.
- $model_preference = '';
- if ( method_exists( $runner, 'get_ai_model_preference' ) ) {
- $model_preference = $runner->get_ai_model_preference();
+ $analysis = $this->run_name_analysis( $model_preference, $plugin_data['name'], $plugin_data['author'] );
+ $this->handle_analysis_response( $result, $analysis, $plugin_data['file'] );
+ }
+
+ /**
+ * Determines if the AI check should run.
+ *
+ * @since 2.1.0
+ *
+ * @param mixed $runner The active runner.
+ * @return bool True if the check should run, false otherwise.
+ */
+ private function should_run_ai_check( $runner ) {
+ if ( ! $runner ) {
+ return false;
+ }
+ if ( ! method_exists( $runner, 'should_use_ai' ) ) {
+ return false;
+ }
+ if ( ! $runner->should_use_ai() ) {
+ return false;
+ }
+ if ( is_wp_error( $this->check_ai_prerequisites() ) ) {
+ return false;
+ }
+ if ( is_wp_error( $this->check_ai_connectors() ) ) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Gets the selected AI model preference.
+ *
+ * @since 2.1.0
+ *
+ * @param mixed $runner The active runner.
+ * @return string The model preference.
+ */
+ private function get_model_preference( $runner ) {
+ $model = '';
+ if ( $runner && method_exists( $runner, 'get_ai_model_preference' ) ) {
+ $model = $runner->get_ai_model_preference();
}
- if ( empty( $model_preference ) && class_exists( Settings_Page::class ) ) {
- $model_preference = Settings_Page::get_model_preference();
+ if ( empty( $model ) && class_exists( Settings_Page::class ) ) {
+ $model = Settings_Page::get_model_preference();
}
+ return $model;
+ }
+ /**
+ * Gets the plugin name and author from headers.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @return array|null Name and author if found, null otherwise.
+ */
+ private function get_plugin_name_and_author( Check_Result $result ) {
if ( ! function_exists( 'get_plugin_data' ) ) {
require_once ABSPATH . 'wp-admin/includes/plugin.php';
}
- $plugin_main_file = $result->plugin()->main_file();
- $plugin_header = get_plugin_data( $plugin_main_file );
- $plugin_name = isset( $plugin_header['Name'] ) ? $plugin_header['Name'] : '';
- $author_name = isset( $plugin_header['AuthorName'] ) ? $plugin_header['AuthorName'] : '';
+ $file = $result->plugin()->main_file();
+ $header = get_plugin_data( $file );
+ $name = isset( $header['Name'] ) ? $header['Name'] : '';
+ $author = isset( $header['AuthorName'] ) ? $header['AuthorName'] : '';
- if ( empty( $plugin_name ) ) {
- return;
+ if ( empty( $name ) ) {
+ return null;
}
- $analysis = $this->run_name_analysis( $model_preference, $plugin_name, $author_name );
+ return array(
+ 'name' => $name,
+ 'author' => $author,
+ 'file' => $file,
+ );
+ }
+
+ /**
+ * Handles the AI analysis response and parses results.
+ *
+ * @since 2.1.0
+ *
+ * @param Check_Result $result The check result.
+ * @param mixed $analysis The analysis response or WP_Error.
+ * @param string $plugin_main_file The plugin main file path.
+ */
+ private function handle_analysis_response( Check_Result $result, $analysis, string $plugin_main_file ) {
if ( is_wp_error( $analysis ) ) {
$this->add_result_warning_for_file(
$result,
From 462fabf8256571f5d23e7d346506bc070ea18ebb Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Wed, 15 Jul 2026 20:57:51 +0530
Subject: [PATCH 06/10] refactor: separate AI name check option
---
assets/js/plugin-check-admin.js | 19 +++++++++++
includes/Admin/Admin_AJAX.php | 15 +++++----
includes/CLI/Plugin_Check_Command.php | 5 +++
includes/Checker/Abstract_Check_Runner.php | 32 ++++++++++++++++++-
.../Checks/Plugin_Repo/AI_Name_Check.php | 32 +++++++++----------
templates/admin-page.php | 3 ++
.../Checker/Checks/AI_Name_Check_Tests.php | 2 +-
7 files changed, 84 insertions(+), 24 deletions(-)
diff --git a/assets/js/plugin-check-admin.js b/assets/js/plugin-check-admin.js
index 9b17043b3..d0578264f 100644
--- a/assets/js/plugin-check-admin.js
+++ b/assets/js/plugin-check-admin.js
@@ -38,6 +38,7 @@
'plugin-check__include-experimental'
);
const useAi = document.getElementById( 'plugin-check__use-ai' );
+ const useAiName = document.getElementById( 'plugin-check__use-ai-name' );
// Handle disabling the Check it button when a plugin is not selected.
function canRunChecks() {
@@ -140,6 +141,9 @@
if ( useAi ) {
useAi.disabled = true;
}
+ if ( useAiName ) {
+ useAiName.disabled = true;
+ }
if ( includeExperimental ) {
includeExperimental.disabled = true;
}
@@ -220,6 +224,9 @@
if ( useAi ) {
useAi.disabled = false;
}
+ if ( useAiName ) {
+ useAiName.disabled = false;
+ }
if ( includeExperimental ) {
includeExperimental.disabled = false;
}
@@ -654,6 +661,10 @@
includeExperimentalInput
);
pluginCheckData.append( 'use-ai', useAiInput );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < checks.length; i++ ) {
pluginCheckData.append( 'checks[]', checks[ i ] );
@@ -718,6 +729,10 @@
includeExperimentalInput
);
pluginCheckData.append( 'use-ai', useAiInput );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < categories.length; i++ ) {
pluginCheckData.append( 'categories[]', categories[ i ] );
@@ -1010,6 +1025,10 @@
includeExperimentalInput
);
pluginCheckData.append( 'use-ai', useAiInput );
+ pluginCheckData.append(
+ 'use-ai-name',
+ useAiName && useAiName.checked ? 1 : 0
+ );
for ( let i = 0; i < types.length; i++ ) {
pluginCheckData.append( 'types[]', types[ i ] );
diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php
index 3d2b42216..7a2a15979 100644
--- a/includes/Admin/Admin_AJAX.php
+++ b/includes/Admin/Admin_AJAX.php
@@ -218,12 +218,13 @@ public function clean_up_environment() {
public function get_checks_to_run() {
$this->check_request_validity();
- $categories = filter_input( INPUT_POST, 'categories', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
- $categories = is_null( $categories ) ? array() : $categories;
- $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
- $checks = is_null( $checks ) ? array() : $checks;
- $use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
- $runner = $this->get_ajax_runner();
+ $categories = filter_input( INPUT_POST, 'categories', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
+ $categories = is_null( $categories ) ? array() : $categories;
+ $checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
+ $checks = is_null( $checks ) ? array() : $checks;
+ $use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
+ $use_ai_name = 1 === filter_input( INPUT_POST, 'use-ai-name', FILTER_VALIDATE_INT );
+ $runner = $this->get_ajax_runner();
if ( is_wp_error( $runner ) ) {
wp_send_json_error( $runner, 500 );
@@ -285,6 +286,7 @@ public function run_checks() {
$include_experimental = 1 === filter_input( INPUT_POST, 'include-experimental', FILTER_VALIDATE_INT );
$use_ai = 1 === filter_input( INPUT_POST, 'use-ai', FILTER_VALIDATE_INT );
+ $use_ai_name = 1 === filter_input( INPUT_POST, 'use-ai-name', FILTER_VALIDATE_INT );
$types = filter_input( INPUT_POST, 'types', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
$types = is_null( $types ) ? array( 'error', 'warning' ) : $types;
@@ -293,6 +295,7 @@ public function run_checks() {
$runner->set_check_slugs( $checks );
$runner->set_plugin( $plugin );
$runner->set_use_ai( $use_ai );
+ $runner->set_use_ai_name( $use_ai_name );
$results = $runner->run();
} catch ( Exception $error ) {
wp_send_json_error(
diff --git a/includes/CLI/Plugin_Check_Command.php b/includes/CLI/Plugin_Check_Command.php
index fd10abbef..a792e0bb7 100644
--- a/includes/CLI/Plugin_Check_Command.php
+++ b/includes/CLI/Plugin_Check_Command.php
@@ -157,6 +157,9 @@ public function __construct( Plugin_Context $plugin_context ) {
* [--ai-model=]
* : AI model preference for analysis (e.g., 'openai::gpt-4o'). Requires --ai.
*
+ * [--ai-name]
+ * : Enable AI-based plugin name checking.
+ *
* ## EXAMPLES
*
* wp plugin check akismet
@@ -201,6 +204,7 @@ public function check( $args, $assoc_args ) {
'mode' => 'new',
'ai' => false,
'ai-model' => '',
+ 'ai-name' => false,
)
);
@@ -262,6 +266,7 @@ static function ( $dirs ) use ( $excluded_files ) {
$runner->set_slug( $options['slug'] );
$runner->set_mode( $options['mode'] );
$runner->set_use_ai( $options['ai'] );
+ $runner->set_use_ai_name( $options['ai-name'] );
if ( ! empty( $options['ai-model'] ) ) {
$runner->set_ai_model_preference( $options['ai-model'] );
}
diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php
index 5040bec8b..510e628b0 100644
--- a/includes/Checker/Abstract_Check_Runner.php
+++ b/includes/Checker/Abstract_Check_Runner.php
@@ -42,6 +42,14 @@ abstract class Abstract_Check_Runner implements Check_Runner {
*/
protected $use_ai = false;
+ /**
+ * Whether AI plugin name checking is enabled.
+ *
+ * @since x.x.x
+ * @var bool
+ */
+ protected $use_ai_name = false;
+
/**
* AI model preference for analysis.
*
@@ -324,6 +332,17 @@ final public function set_use_ai( $use_ai ) {
$this->use_ai = (bool) $use_ai;
}
+ /**
+ * Sets whether to use AI plugin name check.
+ *
+ * @since x.x.x
+ *
+ * @param bool $use_ai_name True to enable AI name check, false to disable.
+ */
+ final public function set_use_ai_name( $use_ai_name ) {
+ $this->use_ai_name = (bool) $use_ai_name;
+ }
+
/**
* Sets the AI model preference for analysis.
*
@@ -347,10 +366,21 @@ public function should_use_ai() {
return $this->use_ai;
}
+ /**
+ * Determines if AI plugin name check should be used.
+ *
+ * @since x.x.x
+ *
+ * @return bool True if AI name check should be used, false otherwise.
+ */
+ public function should_use_ai_name() {
+ return $this->use_ai_name;
+ }
+
/**
* Gets the AI model preference for analysis.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return string Model preference.
*/
diff --git a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
index 384fe68cf..10aa17eab 100644
--- a/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
+++ b/includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php
@@ -20,7 +20,7 @@
/**
* Check for plugin name issues using AI.
*
- * @since 2.1.0
+ * @since x.x.x
*/
class AI_Name_Check implements Static_Check {
@@ -32,7 +32,7 @@ class AI_Name_Check implements Static_Check {
/**
* Gets the categories for the check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return array The categories for the check.
*/
@@ -43,7 +43,7 @@ public function get_categories() {
/**
* Runs the AI Name Check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result to amend.
*/
@@ -66,7 +66,7 @@ public function run( Check_Result $result ) {
/**
* Determines if the AI check should run.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param mixed $runner The active runner.
* @return bool True if the check should run, false otherwise.
@@ -75,10 +75,10 @@ private function should_run_ai_check( $runner ) {
if ( ! $runner ) {
return false;
}
- if ( ! method_exists( $runner, 'should_use_ai' ) ) {
+ if ( ! method_exists( $runner, 'should_use_ai_name' ) ) {
return false;
}
- if ( ! $runner->should_use_ai() ) {
+ if ( ! $runner->should_use_ai_name() ) {
return false;
}
if ( is_wp_error( $this->check_ai_prerequisites() ) ) {
@@ -93,7 +93,7 @@ private function should_run_ai_check( $runner ) {
/**
* Gets the selected AI model preference.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param mixed $runner The active runner.
* @return string The model preference.
@@ -112,7 +112,7 @@ private function get_model_preference( $runner ) {
/**
* Gets the plugin name and author from headers.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @return array|null Name and author if found, null otherwise.
@@ -141,7 +141,7 @@ private function get_plugin_name_and_author( Check_Result $result ) {
/**
* Handles the AI analysis response and parses results.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param mixed $analysis The analysis response or WP_Error.
@@ -169,7 +169,7 @@ private function handle_analysis_response( Check_Result $result, $analysis, stri
/**
* Process analysis results and add appropriate warnings/errors.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $parsed The parsed analysis data.
@@ -191,7 +191,7 @@ private function process_analysis_results( Check_Result $result, array $parsed,
/**
* Check if the plugin name is disallowed.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $data The parsed analysis data.
@@ -207,7 +207,7 @@ private function check_disallowed_name( Check_Result $result, array $data, strin
/**
* Check for possible naming issues.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $data The parsed analysis data.
@@ -223,7 +223,7 @@ private function check_naming_issues( Check_Result $result, array $data, string
/**
* Check for possible owner issues.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $data The parsed analysis data.
@@ -239,7 +239,7 @@ private function check_owner_issues( Check_Result $result, array $data, string $
/**
* Check for similar plugins.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @param Check_Result $result The check result.
* @param array $parsed The parsed analysis data.
@@ -267,7 +267,7 @@ private function check_similar_plugins( Check_Result $result, array $parsed, str
/**
* Gets the description for the check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return string Description.
*/
@@ -278,7 +278,7 @@ public function get_description(): string {
/**
* Gets the documentation URL for the check.
*
- * @since 2.1.0
+ * @since x.x.x
*
* @return string The documentation URL.
*/
diff --git a/templates/admin-page.php b/templates/admin-page.php
index d8bfe935e..26183e659 100644
--- a/templates/admin-page.php
+++ b/templates/admin-page.php
@@ -85,6 +85,9 @@
+
+
+
diff --git a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
index 759cd6d1f..29be2abc0 100644
--- a/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
+++ b/tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php
@@ -11,7 +11,7 @@ class AI_Name_Check_Tests extends \WP_UnitTestCase {
public function test_run_bails_early_when_ai_disabled() {
$runner = $this->createMock( AJAX_Runner::class );
- $runner->method( 'should_use_ai' )->willReturn( false );
+ $runner->method( 'should_use_ai_name' )->willReturn( false );
$reflection = new \ReflectionClass( Plugin_Request_Utility::class );
$property = $reflection->getProperty( 'runner' );
From 6c8ecc01a431eb48d99812d5a05ab4aee2082f58 Mon Sep 17 00:00:00 2001
From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com>
Date: Wed, 15 Jul 2026 21:04:53 +0530
Subject: [PATCH 07/10] fix: resolve phpmd warning
---
includes/Admin/Admin_AJAX.php | 1 +
includes/Checker/Abstract_Check_Runner.php | 1 +
2 files changed, 2 insertions(+)
diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php
index 7a2a15979..cf9f87544 100644
--- a/includes/Admin/Admin_AJAX.php
+++ b/includes/Admin/Admin_AJAX.php
@@ -234,6 +234,7 @@ public function get_checks_to_run() {
$this->configure_runner( $runner );
$runner->set_categories( $categories );
$runner->set_use_ai( $use_ai );
+ $runner->set_use_ai_name( $use_ai_name );
$checks_to_run = $runner->get_checks_to_run();
} catch ( Exception $error ) {
diff --git a/includes/Checker/Abstract_Check_Runner.php b/includes/Checker/Abstract_Check_Runner.php
index 510e628b0..69c1ecf19 100644
--- a/includes/Checker/Abstract_Check_Runner.php
+++ b/includes/Checker/Abstract_Check_Runner.php
@@ -21,6 +21,7 @@
*
* @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
* @SuppressWarnings(PHPMD.TooManyPublicMethods)
+ * @SuppressWarnings(PHPMD.TooManyFields)
*/
abstract class Abstract_Check_Runner implements Check_Runner {
From ce4359f2ac32e55be103a281528f45be29cc1247 Mon Sep 17 00:00:00 2001
From: davidperezgar
Date: Sat, 22 Aug 2026 11:09:53 +0200
Subject: [PATCH 08/10] fix: remove leftover conflict markers
---
includes/Plugin_Main.php | 4 ----
1 file changed, 4 deletions(-)
diff --git a/includes/Plugin_Main.php b/includes/Plugin_Main.php
index cfe422fc3..4296f1aad 100644
--- a/includes/Plugin_Main.php
+++ b/includes/Plugin_Main.php
@@ -78,12 +78,8 @@ public function add_hooks() {
// Create the Settings page.
$settings_page = new Settings_Page();
$settings_page->add_hooks();
-<<<<<<< HEAD
-
// Create the SVN Checker page.
$svn_checker_page = new SVN_Checker_Page();
$svn_checker_page->add_hooks();
-=======
->>>>>>> 443d8a22 (feat: integrate Plugin Check Namer as standard AI Name Check static check)
}
}
From eb7084ff24ba0ef29d98e2948ee15277b09de425 Mon Sep 17 00:00:00 2001
From: davidperezgar
Date: Sat, 22 Aug 2026 11:59:24 +0200
Subject: [PATCH 09/10] finished
---
includes/Admin/Admin_AJAX.php | 18 ++++--------------
includes/Utilities/Plugin_Request_Utility.php | 15 +++++++++++++++
templates/admin-page.php | 6 +++---
3 files changed, 22 insertions(+), 17 deletions(-)
diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php
index cf9f87544..b26fd2959 100644
--- a/includes/Admin/Admin_AJAX.php
+++ b/includes/Admin/Admin_AJAX.php
@@ -143,6 +143,10 @@ private function get_ajax_runner() {
return new WP_Error( 'invalid-runner', __( 'AJAX Runner was not initialized correctly.', 'plugin-check' ) );
}
+ // Register the runner so that checks relying on Plugin_Request_Utility::get_runner()
+ // (e.g. AI_Name_Check) can access it, even when no runtime checks are involved.
+ Plugin_Request_Utility::set_runner( $runner );
+
return $runner;
}
@@ -267,20 +271,6 @@ public function run_checks() {
wp_send_json_error( $runner, 500 );
}
- $runner = Plugin_Request_Utility::get_runner();
-
- if ( is_null( $runner ) ) {
- $runner = new AJAX_Runner();
- }
-
- // Make sure we are using the correct runner instance.
- if ( ! ( $runner instanceof AJAX_Runner ) ) {
- wp_send_json_error(
- new WP_Error( 'invalid-runner', __( 'AJAX Runner was not initialized correctly.', 'plugin-check' ) ),
- 500
- );
- }
-
$checks = filter_input( INPUT_POST, 'checks', FILTER_DEFAULT, FILTER_FORCE_ARRAY );
$checks = is_null( $checks ) ? array() : $checks;
$plugin = filter_input( INPUT_POST, 'plugin', FILTER_SANITIZE_FULL_SPECIAL_CHARS );
diff --git a/includes/Utilities/Plugin_Request_Utility.php b/includes/Utilities/Plugin_Request_Utility.php
index 3451f8052..8958a982c 100644
--- a/includes/Utilities/Plugin_Request_Utility.php
+++ b/includes/Utilities/Plugin_Request_Utility.php
@@ -143,6 +143,21 @@ public static function get_runner() {
return null;
}
+ /**
+ * Sets the Runner class for the current request.
+ *
+ * This is needed for requests (e.g. AJAX requests running only static checks)
+ * where the runner is not registered early via the object-cache.php drop-in,
+ * so that checks relying on get_runner() (such as AI_Name_Check) can access it.
+ *
+ * @since x.x.x
+ *
+ * @param Abstract_Check_Runner $runner The runner instance for the current request.
+ */
+ public static function set_runner( Abstract_Check_Runner $runner ) {
+ static::$runner = $runner;
+ }
+
/**
* Runs the cleanup functions and destroys the runner.
*
diff --git a/templates/admin-page.php b/templates/admin-page.php
index 26183e659..e3ff17ee6 100644
--- a/templates/admin-page.php
+++ b/templates/admin-page.php
@@ -81,12 +81,12 @@
-
+
-
+
-
+
From 685e020b48b3239da8a32a856e0774fedc8b9637 Mon Sep 17 00:00:00 2001
From: davidperezgar
Date: Sat, 22 Aug 2026 12:16:13 +0200
Subject: [PATCH 10/10] fix: suppress PHPMD TooManyPublicMethods warning in
Plugin_Request_Utility
The new set_runner() method (added to support AI Name Check in AJAX-only
static check requests) pushed the public method count above PHPMD's
threshold. Suppressing the rule here follows the same pattern already used
in Admin_Page, Settings_Page, Abstract_Check_Runner and Check_Result.
---
includes/Utilities/Plugin_Request_Utility.php | 2 ++
1 file changed, 2 insertions(+)
diff --git a/includes/Utilities/Plugin_Request_Utility.php b/includes/Utilities/Plugin_Request_Utility.php
index 8958a982c..fd05763ec 100644
--- a/includes/Utilities/Plugin_Request_Utility.php
+++ b/includes/Utilities/Plugin_Request_Utility.php
@@ -16,6 +16,8 @@
* Class providing utility methods to return plugin information based on the request.
*
* @since 1.0.0
+ *
+ * @SuppressWarnings(PHPMD.TooManyPublicMethods)
*/
class Plugin_Request_Utility {