From 443d8a229646e8a2379b2a14743097a77af00640 Mon Sep 17 00:00:00 2001 From: Ishita Jagtap <143935560+ishitaj34@users.noreply.github.com> Date: Thu, 9 Jul 2026 19:41:20 +0530 Subject: [PATCH 1/7] feat: integrate Plugin Check Namer as standard AI Name Check static check --- assets/js/plugin-check-namer.js | 432 ---------------- includes/Admin/Namer_Page.php | 460 ------------------ includes/Checker/Abstract_Check_Runner.php | 13 +- .../Checks/Plugin_Repo/AI_Name_Check.php | 179 +++++++ includes/Checker/Default_Check_Repository.php | 1 + includes/Plugin_Main.php | 5 - .../Checker/Checks/AI_Name_Check_Tests.php | 33 ++ 7 files changed, 225 insertions(+), 898 deletions(-) delete mode 100644 assets/js/plugin-check-namer.js delete mode 100644 includes/Admin/Namer_Page.php create mode 100644 includes/Checker/Checks/Plugin_Repo/AI_Name_Check.php create mode 100644 tests/phpunit/tests/Checker/Checks/AI_Name_Check_Tests.php diff --git a/assets/js/plugin-check-namer.js b/assets/js/plugin-check-namer.js deleted file mode 100644 index f8e3d90cb..000000000 --- a/assets/js/plugin-check-namer.js +++ /dev/null @@ -1,432 +0,0 @@ -/** - * Plugin Check Namer tool. - */ - -/* global pluginCheckNamer */ - -( function () { - 'use strict'; - - function setText( el, text ) { - if ( ! el ) { - return; - } - el.textContent = text; - } - - function setHtml( el, html ) { - if ( ! el ) { - return; - } - el.innerHTML = html; - } - - function escapeHtml( text ) { - const div = document.createElement( 'div' ); - div.textContent = text; - return div.innerHTML; - } - - document.addEventListener( 'DOMContentLoaded', function () { - const form = document.getElementById( 'plugin-check-namer-form' ); - const input = document.getElementById( 'plugin_check_namer_input' ); - - if ( ! form || ! input || ! window.pluginCheckNamer ) { - return; - } - - const authorInput = document.getElementById( - 'plugin_check_namer_author' - ); - const modelSelect = document.getElementById( - 'plugin_check_namer_model' - ); - - const submitBtn = document.getElementById( - 'plugin-check-namer-submit' - ); - const spinner = document.getElementById( 'plugin-check-namer-spinner' ); - - const resultWrap = document.getElementById( - 'plugin-check-namer-result' - ); - const verdictEl = document.getElementById( - 'plugin-check-namer-verdict' - ); - const explainEl = document.getElementById( - 'plugin-check-namer-explanation' - ); - const verdictContainer = document.getElementById( - 'plugin-check-namer-verdict-container' - ); - const confusionPluginsDiv = document.getElementById( - 'plugin-check-namer-confusion-plugins' - ); - const confusionPluginsList = document.getElementById( - 'plugin-check-namer-confusion-plugins-list' - ); - const confusionOthersDiv = document.getElementById( - 'plugin-check-namer-confusion-others' - ); - const confusionOthersList = document.getElementById( - 'plugin-check-namer-confusion-others-list' - ); - const timingDiv = document.getElementById( - 'plugin-check-namer-timing' - ); - const timingValue = document.getElementById( - 'plugin-check-namer-timing-value' - ); - const tokensDiv = document.getElementById( - 'plugin-check-namer-tokens' - ); - const tokensValue = document.getElementById( - 'plugin-check-namer-tokens-value' - ); - const errorDiv = document.getElementById( 'plugin-check-namer-error' ); - const errorEl = errorDiv ? errorDiv.querySelector( 'p' ) : null; - - function setLoading( isLoading ) { - if ( spinner ) { - spinner.classList.toggle( 'is-active', isLoading ); - } - submitBtn.disabled = isLoading; - } - - form.addEventListener( 'submit', function ( event ) { - event.preventDefault(); - - const name = ( input.value || '' ).trim(); - if ( ! name ) { - setText( errorEl, pluginCheckNamer.messages.missingName ); - if ( errorEl ) { - errorEl.classList.remove( 'plugin-check-namer-hidden' ); - } - return; - } - - // Clear previous results. - if ( resultWrap ) { - resultWrap.classList.add( 'plugin-check-namer-hidden' ); - } - if ( verdictEl ) { - setText( verdictEl, '' ); - } - if ( explainEl ) { - setHtml( explainEl, '' ); - } - if ( errorDiv ) { - errorDiv.classList.add( 'plugin-check-namer-hidden' ); - } - if ( errorEl ) { - setText( errorEl, '' ); - } - if ( confusionPluginsDiv ) { - confusionPluginsDiv.classList.add( - 'plugin-check-namer-hidden' - ); - } - if ( confusionPluginsList ) { - confusionPluginsList.innerHTML = ''; - } - if ( confusionOthersDiv ) { - confusionOthersDiv.classList.add( 'plugin-check-namer-hidden' ); - } - if ( confusionOthersList ) { - confusionOthersList.innerHTML = ''; - } - if ( timingDiv ) { - timingDiv.classList.add( 'plugin-check-namer-hidden' ); - } - if ( timingValue ) { - setText( timingValue, '' ); - } - if ( tokensDiv ) { - tokensDiv.classList.add( 'plugin-check-namer-hidden' ); - } - if ( tokensValue ) { - setText( tokensValue, '' ); - } - if ( verdictContainer ) { - verdictContainer.classList.add( 'plugin-check-namer-hidden' ); - } - - // Record start time. - const startTime = Date.now(); - - setLoading( true ); - - const formData = new FormData(); - formData.append( 'action', 'plugin_check_namer_analyze' ); - formData.append( 'nonce', pluginCheckNamer.nonce ); - formData.append( 'plugin_name', name ); - if ( authorInput ) { - formData.append( - 'author_name', - ( authorInput.value || '' ).trim() - ); - } - if ( modelSelect ) { - formData.append( - 'model_preference', - ( modelSelect.value || '' ).trim() - ); - } - - fetch( pluginCheckNamer.ajaxUrl, { - method: 'POST', - credentials: 'same-origin', - body: formData, - } ) - .then( function ( response ) { - return response.json(); - } ) - .then( function ( payload ) { - if ( ! payload || ! payload.success ) { - throw new Error( - payload && payload.data && payload.data.message - ? payload.data.message - : pluginCheckNamer.messages.genericError - ); - } - - setText( verdictEl, payload.data.verdict || '' ); - setHtml( explainEl, payload.data.explanation || '' ); - - // Set border color based on verdict. - if ( verdictContainer ) { - const verdict = ( - payload.data.verdict || '' - ).toLowerCase(); - let borderColor = '#2271b1'; // Default blue. - - if ( verdict.indexOf( 'disallowed' ) !== -1 ) { - borderColor = '#d63638'; // Red for disallowed. - } else if ( - verdict.indexOf( 'good' ) !== -1 || - verdict.indexOf( 'low' ) !== -1 || - verdict.indexOf( 'no issues' ) !== -1 - ) { - borderColor = '#00a32a'; // Green for good. - } else if ( - verdict.indexOf( 'generally allowable' ) !== -1 || - verdict.indexOf( 'allowable' ) !== -1 - ) { - borderColor = '#2271b1'; // Blue for generally allowable. - } else if ( - verdict.indexOf( 'review' ) !== -1 || - verdict.indexOf( 'medium' ) !== -1 || - verdict.indexOf( 'issues found' ) !== -1 - ) { - borderColor = '#dba617'; // Yellow/orange for needs review. - } else if ( - verdict.indexOf( 'problematic' ) !== -1 || - verdict.indexOf( 'high' ) !== -1 - ) { - borderColor = '#d63638'; // Red for problematic. - } - - verdictContainer.style.borderLeftColor = borderColor; - verdictContainer.classList.remove( - 'plugin-check-namer-hidden' - ); - } - - // Calculate and display elapsed time. - if ( timingDiv && timingValue ) { - const endTime = Date.now(); - const elapsedSeconds = Math.round( - ( endTime - startTime ) / 1000 - ); - timingValue.textContent = - elapsedSeconds + ' ' + 'seconds'; - timingDiv.classList.remove( - 'plugin-check-namer-hidden' - ); - } - - // Display token usage if available. - if ( - tokensDiv && - tokensValue && - payload.data.token_usage - ) { - const tokenUsage = payload.data.token_usage; - let tokensText = ''; - - // Add AI provider and model info if available. - if ( payload.data.ai_info ) { - const parts = []; - if ( payload.data.ai_info.provider ) { - parts.push( payload.data.ai_info.provider ); - } - if ( payload.data.ai_info.model ) { - parts.push( payload.data.ai_info.model ); - } - if ( parts.length ) { - tokensText = parts.join( ' / ' ) + ' - '; - } - } - - const totalTokens = - tokenUsage.total_tokens || - ( tokenUsage.prompt_tokens && - tokenUsage.completion_tokens - ? tokenUsage.prompt_tokens + - tokenUsage.completion_tokens - : null ); - - if ( totalTokens ) { - tokensText += totalTokens + ' total'; - } - - // Add breakdown with prompt and completion tokens. - if ( - tokenUsage.prompt_tokens && - tokenUsage.completion_tokens - ) { - tokensText += - ' (' + - tokenUsage.prompt_tokens + - ' prompt + ' + - tokenUsage.completion_tokens + - ' completion)'; - } - - // Add similar name query tokens if available. - if ( - tokenUsage.similar_name && - tokenUsage.similar_name.total_tokens - ) { - tokensText += - ' [Similar name query: ' + - tokenUsage.similar_name.total_tokens + - ' tokens]'; - } - - tokensValue.textContent = tokensText; - tokensDiv.classList.remove( - 'plugin-check-namer-hidden' - ); - } - - // Display confusion_existing_plugins if available. - if ( - confusionPluginsDiv && - confusionPluginsList && - payload.data.confusion_existing_plugins && - payload.data.confusion_existing_plugins.length > 0 - ) { - confusionPluginsList.innerHTML = ''; - payload.data.confusion_existing_plugins.forEach( - function ( plugin ) { - const div = document.createElement( 'div' ); - div.className = - 'plugin-check-namer-confusion-item'; - div.innerHTML = - '' + - escapeHtml( plugin.name || '' ) + - '' + - ( plugin.active_installations - ? ' (' + - escapeHtml( - plugin.active_installations - ) + - ' ' + - 'active installs' + - ')' - : '' ) + - ( plugin.owner_username - ? ' - ' + - escapeHtml( plugin.owner_username ) + - '' - : '' ) + - '
' + - '' + - escapeHtml( plugin.explanation || '' ) + - '' + - ( plugin.link - ? '
' + - escapeHtml( plugin.link ) + - '' - : '' ); - confusionPluginsList.appendChild( div ); - } - ); - confusionPluginsDiv.classList.remove( - 'plugin-check-namer-hidden' - ); - } else if ( confusionPluginsDiv ) { - confusionPluginsDiv.classList.add( - 'plugin-check-namer-hidden' - ); - } - - // Display confusion_existing_others if available. - if ( - confusionOthersDiv && - confusionOthersList && - payload.data.confusion_existing_others && - payload.data.confusion_existing_others.length > 0 - ) { - confusionOthersList.innerHTML = ''; - payload.data.confusion_existing_others.forEach( - function ( item ) { - const div = document.createElement( 'div' ); - div.className = - 'plugin-check-namer-confusion-item plugin-check-namer-confusion-item-others'; - div.innerHTML = - '' + - escapeHtml( item.name || '' ) + - '' + - '
' + - '' + - escapeHtml( item.explanation || '' ) + - '' + - ( item.link - ? '
' + - escapeHtml( item.link ) + - '' - : '' ); - confusionOthersList.appendChild( div ); - } - ); - confusionOthersDiv.classList.remove( - 'plugin-check-namer-hidden' - ); - } else if ( confusionOthersDiv ) { - confusionOthersDiv.classList.add( - 'plugin-check-namer-hidden' - ); - } - - if ( resultWrap ) { - resultWrap.classList.remove( - 'plugin-check-namer-hidden' - ); - } - } ) - .catch( function ( err ) { - setText( - errorEl, - err && err.message - ? err.message - : pluginCheckNamer.messages.genericError - ); - - if ( errorDiv ) { - errorDiv.classList.remove( - 'plugin-check-namer-hidden' - ); - } - } ) - .finally( function () { - setLoading( false ); - } ); - } ); - } ); -} )(); diff --git a/includes/Admin/Namer_Page.php b/includes/Admin/Namer_Page.php deleted file mode 100644 index cb4b0d415..000000000 --- a/includes/Admin/Namer_Page.php +++ /dev/null @@ -1,460 +0,0 @@ -hook_suffix = add_management_page( - __( 'Plugin Check Namer', 'plugin-check' ), - __( 'Plugin Check Namer', 'plugin-check' ), - 'manage_options', - self::MENU_SLUG, - array( $this, 'render_page' ) - ); - } - - /** - * Enqueues scripts for the tools page. - * - * @since 1.8.0 - * - * @param string $hook_suffix Current admin page hook suffix. - */ - public function enqueue_scripts( $hook_suffix ) { - if ( $hook_suffix !== $this->hook_suffix ) { - return; - } - - wp_enqueue_style( - 'plugin-check-admin', - plugins_url( 'assets/css/plugin-check-admin.css', WP_PLUGIN_CHECK_MAIN_FILE ), - array(), - WP_PLUGIN_CHECK_VERSION - ); - - wp_enqueue_script( - 'plugin-check-namer', - plugins_url( 'assets/js/plugin-check-namer.js', WP_PLUGIN_CHECK_MAIN_FILE ), - array(), - WP_PLUGIN_CHECK_VERSION, - true - ); - - wp_localize_script( - 'plugin-check-namer', - 'pluginCheckNamer', - array( - 'ajaxUrl' => admin_url( 'admin-ajax.php' ), - 'nonce' => wp_create_nonce( 'plugin_check_namer_ajax' ), - 'messages' => array( - 'missingName' => __( 'Please enter a plugin name.', 'plugin-check' ), - 'genericError' => __( 'An unexpected error occurred.', 'plugin-check' ), - ), - ) - ); - } - - /** - * AJAX handler to analyze a plugin name. - * - * @since 1.8.0 - */ - public function ajax_analyze() { - check_ajax_referer( 'plugin_check_namer_ajax', 'nonce' ); - - if ( ! current_user_can( 'manage_options' ) ) { - wp_send_json_error( array( 'message' => __( 'Insufficient permissions.', 'plugin-check' ) ) ); - } - - $name = $this->get_plugin_name_from_request(); - if ( empty( $name ) ) { - wp_send_json_error( array( 'message' => __( 'Please enter a plugin name.', 'plugin-check' ) ) ); - } - - $author = $this->get_author_name_from_request(); - - $model_preference = $this->get_model_preference_from_request(); - $ai_config = $this->get_ai_config( $model_preference ); - if ( is_wp_error( $ai_config ) ) { - wp_send_json_error( array( 'message' => $ai_config->get_error_message() ) ); - } - - $analysis = $this->run_name_analysis( $ai_config['model_preference'], $name, $author ); - if ( is_wp_error( $analysis ) ) { - wp_send_json_error( array( 'message' => $analysis->get_error_message() ) ); - } - - $parsed = $this->parse_analysis( $analysis ); - $response = $this->build_ajax_response( $parsed, $analysis, $ai_config ); - - wp_send_json_success( $response ); - } - - /** - * Builds AJAX response from parsed analysis. - * - * @since 1.8.0 - * - * @param array $parsed Parsed analysis. - * @param string|array $analysis Raw analysis. - * @param array $ai_config AI configuration with model preference info. - * @return array Response array. - */ - protected function build_ajax_response( $parsed, $analysis, $ai_config = array() ) { - $raw_output = $this->get_raw_output( $parsed, $analysis ); - $raw_output = $this->format_json_output( $raw_output ); - - $response = array( - 'verdict' => $parsed['verdict'], - 'explanation' => $parsed['explanation'], - 'raw' => $raw_output, - ); - - if ( ! empty( $parsed['confusion_existing_plugins'] ) ) { - $response['confusion_existing_plugins'] = $parsed['confusion_existing_plugins']; - } - if ( ! empty( $parsed['confusion_existing_others'] ) ) { - $response['confusion_existing_others'] = $parsed['confusion_existing_others']; - } - if ( ! empty( $parsed['token_usage'] ) ) { - $response['token_usage'] = $parsed['token_usage']; - } - - // Add AI model preference information. - if ( ! empty( $ai_config['model_preference'] ) ) { - $response['ai_info'] = array( - 'model' => $ai_config['model_preference'], - ); - } - - return $response; - } - - /** - * Gets plugin name from request. - * - * @since 1.8.0 - * - * @return string Plugin name or empty string. - */ - protected function get_plugin_name_from_request() { - $name = isset( $_POST['plugin_name'] ) ? sanitize_text_field( wp_unslash( $_POST['plugin_name'] ) ) : ''; - return trim( $name ); - } - - /** - * Gets author name from request. - * - * @since 1.8.0 - * - * @return string Author name or empty string. - */ - protected function get_author_name_from_request() { - $author = isset( $_POST['author_name'] ) ? sanitize_text_field( wp_unslash( $_POST['author_name'] ) ) : ''; - return trim( $author ); - } - - /** - * Renders the page. - * - * @since 1.8.0 - * - * @SuppressWarnings(PHPMD.ExcessiveMethodLength) - */ - public function render_page() { - if ( ! current_user_can( 'manage_options' ) ) { - return; - } - - $ai_config = $this->get_ai_config(); - ?> -
-

- - -
-

- get_error_message() ); - ?> -

-
- - check_ai_prerequisites() && is_wp_error( $this->check_ai_connectors() ) ) { - ?> -

- - - -

- - -
- - - - - - - - - - - - - - - - - -

- -
- -
- -

-

- - -

-
- -

- -
-

-
-

- - -

-

- - -

-

- - -

-

- - -

-
-
-

-
-
- -
-

-
-
-
-
- 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 c1b7d420c..eb1a81f96 100644 --- a/includes/Checker/Default_Check_Repository.php +++ b/includes/Checker/Default_Check_Repository.php @@ -103,6 +103,7 @@ private function register_default_checks() { 'direct_file_access' => new Checks\Plugin_Repo\Direct_File_Access_Check(), '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_name' => new Checks\Plugin_Repo\AI_Name_Check(), ) ); diff --git a/includes/Plugin_Main.php b/includes/Plugin_Main.php index 3aa0a4343..d211a1afd 100644 --- a/includes/Plugin_Main.php +++ b/includes/Plugin_Main.php @@ -77,10 +77,5 @@ 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(); } } 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 173023daa3eae98d0bc404f9a2a5d5455bf54661 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 2/7] 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 3/7] 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 53e33b98f8f68a9054d6ff0d3720db94e873bf64 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 4/7] 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 b4095ea5abb6a84b2e7671556b9dd6f15fc6e9d8 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 5/7] 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 33e0cf4109de6fb4e6878ed031845e63201a9e5e 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 6/7] 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 22227cc4e..0bc320444 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() { @@ -92,6 +93,9 @@ if ( useAi ) { useAi.disabled = true; } + if ( useAiName ) { + useAiName.disabled = true; + } if ( includeExperimental ) { includeExperimental.disabled = true; } @@ -144,6 +148,9 @@ if ( useAi ) { useAi.disabled = false; } + if ( useAiName ) { + useAiName.disabled = false; + } if ( includeExperimental ) { includeExperimental.disabled = false; } @@ -596,6 +603,10 @@ includeExperimental && includeExperimental.checked ? 1 : 0 ); pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 ); + pluginCheckData.append( + 'use-ai-name', + useAiName && useAiName.checked ? 1 : 0 + ); for ( let i = 0; i < data.checks.length; i++ ) { pluginCheckData.append( 'checks[]', data.checks[ i ] ); @@ -669,6 +680,10 @@ includeExperimental && includeExperimental.checked ? 1 : 0 ); pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 ); + pluginCheckData.append( + 'use-ai-name', + useAiName && useAiName.checked ? 1 : 0 + ); for ( let i = 0; i < categoriesList.length; i++ ) { if ( categoriesList[ i ].checked ) { @@ -957,6 +972,10 @@ includeExperimental && includeExperimental.checked ? 1 : 0 ); pluginCheckData.append( 'use-ai', useAi && useAi.checked ? 1 : 0 ); + pluginCheckData.append( + 'use-ai-name', + useAiName && useAiName.checked ? 1 : 0 + ); for ( let i = 0; i < typesList.length; i++ ) { if ( typesList[ i ].checked ) { diff --git a/includes/Admin/Admin_AJAX.php b/includes/Admin/Admin_AJAX.php index eef695078..06d36bdfe 100644 --- a/includes/Admin/Admin_AJAX.php +++ b/includes/Admin/Admin_AJAX.php @@ -226,12 +226,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 ); @@ -295,6 +296,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; @@ -303,6 +305,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 834478e78..119e5b8d6 100644 --- a/includes/CLI/Plugin_Check_Command.php +++ b/includes/CLI/Plugin_Check_Command.php @@ -149,6 +149,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 @@ -191,6 +194,7 @@ public function check( $args, $assoc_args ) { 'mode' => 'new', 'ai' => false, 'ai-model' => '', + 'ai-name' => false, ) ); @@ -252,6 +256,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 d535e57287b7d7eecc082f9d4a97817a6651ce65 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 7/7] 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 06d36bdfe..cb5afad13 100644 --- a/includes/Admin/Admin_AJAX.php +++ b/includes/Admin/Admin_AJAX.php @@ -242,6 +242,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 ); $plugin_basename = $runner->get_plugin_basename(); $checks_to_run = $runner->get_checks_to_run(); 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 {