#!/usr/bin/env bash set -euo pipefail # ============================================================================== # STRICT WooCommerce Plugin Compatibility Validator # ============================================================================== # This script performs aggressive static analysis to ensure WooCommerce plugins # meet modern standards, specifically HPOS compatibility and security. # ============================================================================== PLUGIN_DIR="${1:-}" # Color codes RED='\033[0;31m' YELLOW='\033[1;33m' GREEN='\033[0;32m' BLUE='\033[0;34m' CYAN='\033[0;36m' MAGENTA='\033[0;35m' NC='\033[0m' # No Color # Counters CRITICAL_COUNT=0 WARNING_COUNT=0 # Usage check if [[ -z "${PLUGIN_DIR}" ]]; then echo "Usage: $(basename "$0") /path/to/plugin" >&2 exit 1 fi if [[ ! -d "${PLUGIN_DIR}" ]]; then echo "Plugin directory not found: ${PLUGIN_DIR}" >&2 exit 1 fi echo -e "${BLUE}========================================================${NC}" echo -e "${BLUE} STRICT WOOCOMMERCE COMPATIBILITY VALIDATION ${NC}" echo -e "${BLUE}========================================================${NC}" echo "Target: ${PLUGIN_DIR}" echo "" # Gather files mapfile -t php_files < <(find "${PLUGIN_DIR}" -type f -name "*.php" ! -path "*/vendor/*" ! -path "*/node_modules/*" 2>/dev/null | sort || true) if [[ "${#php_files[@]}" -eq 0 ]]; then echo -e "${RED}✗ FATAL: No PHP files found.${NC}" exit 1 fi # ============================================ # 1. HPOS STRICT MODE (CRITICAL) # ============================================ echo -e "${MAGENTA}[1/9] HPOS (High-Performance Order Storage) Strict Analysis...${NC}" hpos_declared=false hpos_compatible=false # 1.1 Check Declaration for file in "${php_files[@]}"; do if grep -q "FeaturesUtil::declare_compatibility" "${file}"; then hpos_declared=true if grep -q "'custom_order_tables'" "${file}" || grep -q '"custom_order_tables"' "${file}"; then hpos_compatible=true fi fi done if ! $hpos_declared; then echo -e "${RED} ✗ FAILURE: HPOS compatibility not declared.${NC}" echo " Must use: \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility" CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif ! $hpos_compatible; then echo -e "${RED} ✗ FAILURE: HPOS declared but 'custom_order_tables' missing.${NC}" CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) else echo -e "${GREEN} ✓ HPOS compatibility declared.${NC}" fi # 1.2 Check for Legacy Direct DB Access (Strict) # Flag any direct SQL queries that mention posts table AND shop_order in the same file for file in "${php_files[@]}"; do # Check for SQL queries joining posts/postmeta looking for orders if grep -E "wp_posts|wp_postmeta" "${file}" | grep -qE "shop_order|_order_"; then # Exclude if comments explicitly say it's legacy fallback if ! grep -q "HPOS usage" "${file}"; then echo -e "${RED} ✗ HPOS VIOLATION in ${file}:${NC}" echo " Detected direct 'wp_posts'/'wp_postmeta' access potentially for orders." echo " HPOS requires using WC_Order CRUD methods or specific HPOS tables." grep -nE "wp_posts|wp_postmeta" "${file}" | head -3 CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Check for legacy metadata functions on orders # We flag usage of get_post_meta where the variable name implies an order if grep -nE "(get|update|add|delete)_post_meta\s*\(\s*\\$(order|wc_order|item)" "${file}"; then echo -e "${RED} ✗ HPOS VIOLATION in ${file}:${NC}" echo " Detected legacy meta function on order variable." echo " Use \$order->get_meta(), \$order->update_meta_data(), etc." CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi # Check for direct ID access (e.g., $order->id) if grep -nE "\\\$[a-zA-Z0-9_]*order->id" "${file}" | grep -v "get_id()"; then echo -e "${RED} ✗ DEPRECATED ACCESS in ${file}:${NC}" echo " Detected direct access to \$order->id. Use \$order->get_id()." CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi done echo "" # ============================================ # 2. WOOCOMMERCE VERSION HEADERS # ============================================ echo -e "${MAGENTA}[2/9] Validating Version Headers...${NC}" # Find main plugin file main_file="" for file in "${php_files[@]}"; do if grep -q "Plugin Name:" "${file}"; then main_file="${file}" break fi done if [[ -z "${main_file}" ]]; then echo -e "${RED} ✗ FAILURE: Main plugin file not found.${NC}" CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) else wc_tested=$(grep "WC tested up to:" "${main_file}" | sed 's/.*://' | tr -d '[:space:]') wc_required=$(grep "WC requires at least:" "${main_file}" | sed 's/.*://' | tr -d '[:space:]') if [[ -z "$wc_tested" ]]; then echo -e "${RED} ✗ FAILURE: Missing 'WC tested up to' header.${NC}" CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) else # Simple check if tested version is reasonably recent (starts with 8 or 9) if [[ ! "$wc_tested" =~ ^(8|9)\. ]]; then echo -e "${YELLOW} ⚠ WARNING: 'WC tested up to' ($wc_tested) seems old (pre-8.0).${NC}" WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi if [[ -z "$wc_required" ]]; then echo -e "${RED} ✗ FAILURE: Missing 'WC requires at least' header.${NC}" CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi echo "" # ============================================ # 3. DEPRECATED FUNCTIONS & HOOKS (Strict) # ============================================ echo -e "${MAGENTA}[3/9] Scanning for Deprecated WooCommerce Code...${NC}" DEPRECATED_PATTERNS=( "woocommerce_get_page_id:Use wc_get_page_id()" "wc_get_page_permalink:Use wc_get_page_permalink()" "woocommerce_show_messages:Use wc_print_notices()" "wc_current_theme_is_fse_theme:Use wp_is_block_theme() (WC 9.9+)" "global \$woocommerce:Use WC()" "\$woocommerce->cart:Use WC()->cart" "\$woocommerce->customer:Use WC()->customer" "store_api_validate_add_to_cart:Use woocommerce_store_api_validate_add_to_cart" "jquery-blockui:Use wc-jquery-blockui" "woocommerce_coupon_error:Use wc_add_notice()" "woocommerce_add_notice:Use wc_add_notice()" "woocommerce_get_template:Use wc_get_template()" "woocommerce_get_template_part:Use wc_get_template_part()" "woocommerce_locate_template:Use wc_locate_template()" "WC()->cart->get_cart_from_session:Removed in WC 8.2+" "woocommerce_calculate_totals:Use recalculate_totals()" "woocommerce_get_product_from_item:Use \$item->get_product()" "woocommerce_get_order_item_meta:Use \$item->get_meta()" "woocommerce_downloadable_file_permission:Use wc_downloadable_file_permission()" "woocommerce_sanitize_taxonomy_name:Use wc_sanitize_taxonomy_name()" "woocommerce_clean:Use wc_clean()" "woocommerce_array_overlay:Use wc_array_overlay()" ) for file in "${php_files[@]}"; do for pattern_set in "${DEPRECATED_PATTERNS[@]}"; do pattern="${pattern_set%%:*}" advice="${pattern_set##*:}" if grep -q "$pattern" "${file}"; then echo -e "${RED} ✗ DEPRECATED CODE in ${file}:${NC}" echo " Found: '$pattern'" echo " Fix: $advice" grep -n "$pattern" "${file}" | head -1 CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi done done echo "" # ============================================ # 4. DATABASE & SCHEMA SAFETY # ============================================ echo -e "${MAGENTA}[4/9] Validating Database Usage...${NC}" for file in "${php_files[@]}"; do # Check for creating tables without dbDelta if grep -q "CREATE TABLE" "${file}"; then if ! grep -q "dbDelta" "${file}"; then echo -e "${RED} ✗ DB SAFETY in ${file}:${NC}" echo " 'CREATE TABLE' found without 'dbDelta'. This is a WP standard violation." CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Strict check: Using standard WP tables for order data if grep -E "INSERT INTO.*wp_options" "${file}" | grep -q "woocommerce"; then echo -e "${YELLOW} ⚠ PERFORMANCE: Inserting WooCommerce data directly into wp_options in ${file}.${NC}" echo " Avoid cluttering the autoloaded options table." WARNING_COUNT=$((WARNING_COUNT + 1)) fi done echo "" # ============================================ # 5. TEMPLATE OVERRIDES # ============================================ echo -e "${MAGENTA}[5/9] Checking Template Structure...${NC}" if [[ -d "${PLUGIN_DIR}/templates" ]]; then echo -e "${GREEN} ✓ '/templates' directory found.${NC}" # Check for outdated template versions in comments find "${PLUGIN_DIR}/templates" -name "*.php" | while read -r tpl; do if ! grep -q "@version" "${tpl}"; then echo -e "${YELLOW} ⚠ WARNING: Template ${tpl} missing @version tag.${NC}" WARNING_COUNT=$((WARNING_COUNT + 1)) fi done fi echo "" # ============================================ # 6. CART/CHECKOUT BLOCKS # ============================================ echo -e "${MAGENTA}[6/9] Checking Blocks Compatibility...${NC}" has_blocks_integration=false for file in "${php_files[@]}"; do if grep -qE "woocommerce_blocks|StoreApi|IntegrationInterface" "${file}"; then has_blocks_integration=true break fi done if ! $has_blocks_integration; then echo -e "${YELLOW} ⚠ WARNING: No Cart/Checkout Blocks integration detected.${NC}" echo " Your plugin may break in Block-based checkouts." WARNING_COUNT=$((WARNING_COUNT + 1)) else echo -e "${GREEN} ✓ Blocks integration detected.${NC}" fi echo "" # ============================================ # 7. ORDER NOTES & LOGGING # ============================================ echo -e "${MAGENTA}[7/9] Checking Logging Standards...${NC}" for file in "${php_files[@]}"; do # Check for error_log if grep -q "error_log" "${file}"; then echo -e "${YELLOW} ⚠ WARNING: 'error_log' found in ${file}. Use 'WC_Logger' instead.${NC}" WARNING_COUNT=$((WARNING_COUNT + 1)) fi # Check for adding order notes directly via comments if grep -q "wp_insert_comment" "${file}" | grep -q "order"; then echo -e "${RED} ✗ FAILURE: inserting comments directly for orders in ${file}.${NC}" echo " Use \$order->add_order_note() instead." CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi done echo "" # ============================================ # 8. AJAX ERROR DETECTION # ============================================ echo -e "${MAGENTA}[8/9] Detecting AJAX Errors & Security Issues...${NC}" for file in "${php_files[@]}"; do # Check for AJAX actions without nonce verification if grep -qE "add_action.*wp_ajax_.*add_action.*wp_ajax_nopriv_" "${file}"; then if ! grep -q "check_ajax_referer\|wp_verify_nonce" "${file}"; then echo -e "${RED} ✗ AJAX SECURITY VIOLATION in ${file}:${NC}" echo " AJAX action found without nonce verification." grep -nE "add_action.*wp_ajax_" "${file}" | head -1 CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Check for wp_send_json_error without proper status codes if grep -q "wp_send_json_error" "${file}"; then if ! grep -q "wp_send_json_error.*[45][0-9][0-9]" "${file}"; then echo -e "${YELLOW} ⚠ AJAX ERROR HANDLING in ${file}:${NC}" echo " wp_send_json_error() should include HTTP status code (400, 403, 500)." grep -n "wp_send_json_error" "${file}" | head -1 WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for AJAX handlers using wp_die() or die() if grep -qE "wp_die\(|die\(" "${file}"; then if grep -qE "wp_ajax_.*{" "${file}"; then echo -e "${RED} ✗ AJAX ERROR in ${file}:${NC}" echo " Direct wp_die() or die() in AJAX handler breaks proper error handling." grep -nE "(wp_die|die)\(" "${file}" | head -1 CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Check for AJAX actions missing capability checks if grep -qE "add_action.*wp_ajax_" "${file}"; then if ! grep -qE "current_user_can|check_ajax_referer" "${file}"; then echo -e "${RED} ✗ AJAX SECURITY in ${file}:${NC}" echo " AJAX action missing capability or nonce checks." grep -nE "add_action.*wp_ajax_" "${file}" | head -1 CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Check for AJAX handlers returning data without proper JSON encoding if grep -qE "wp_ajax_" "${file}"; then if grep -q "echo.*json_encode" "${file}"; then echo -e "${YELLOW} ⚠ AJAX ERROR HANDLING in ${file}:${NC}" echo " Manual JSON encoding in AJAX. Use wp_send_json_success/error()." grep -n "echo.*json_encode" "${file}" | head -1 WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for AJAX handlers catching errors without proper logging if grep -qE "wp_ajax_.*try.*catch" "${file}"; then if ! grep -qE "wc_get_logger|wc_log_error|error_log.*AJAX" "${file}"; then echo -e "${YELLOW} ⚠ AJAX ERROR HANDLING in ${file}:${NC}" echo " AJAX try-catch block may not be logging errors properly." grep -nE "wp_ajax_.*try" "${file}" | head -1 WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi done echo "" # ============================================ # 10. CLASS LOADING ERRORS & MISSING CLASSES # ============================================ echo -e "${MAGENTA}[10/10] Detecting Class Loading Errors and Missing Classes...${NC}" for file in "${php_files[@]}"; do # Check for deprecated order properties if grep -qE "\\\$order->(order_type|customer_id|customer_note|post_status)" "${file}"; then echo -e "${RED} ✗ DEPRECATED ORDER PROPERTY in ${file}${NC}" echo " Direct property access removed. Use getter methods." grep -nE "\\\$order->(order_type|customer_id|customer_note|post_status)" "${file}" | head -1 CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi # Check for legacy meta handling on products if grep -E "(get|update|add|delete)_post_meta.*product_id" "${file}"; then if ! grep -q "HPOS" "${file}"; then echo -e "${YELLOW} ⚠ POTENTIAL LEGACY META HANDLING in ${file}${NC}" echo " Verify proper CRUD methods are used for products." WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for proper webhook implementation if grep -q "wc_get_webhook" "${file}" || grep -q "WC_Webhook" "${file}"; then echo -e "${GREEN} ✓ Webhook implementation detected${NC}" fi # Check for payment gateway implementation if grep -q "WC_Payment_Gateway" "${file}"; then if ! grep -qE "process_payment|transaction_result" "${file}"; then echo -e "${YELLOW} ⚠ Incomplete payment gateway in ${file}${NC}" echo " Missing process_payment() method." WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for shipping method implementation if grep -q "WC_Shipping_Method" "${file}"; then if ! grep -qE "calculate_shipping" "${file}"; then echo -e "${YELLOW} ⚠ Incomplete shipping method in ${file}${NC}" echo " Missing calculate_shipping() method." WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for proper cart handling if grep -qE "\\\$woocommerce->cart|WC()->cart->add_to_cart" "${file}"; then if grep -qE "add_to_cart\(\s*\\\$_POST|add_to_cart\(\s*\\\$_GET" "${file}"; then echo -e "${RED} ✗ SECURITY: Direct user input in add_to_cart in ${file}${NC}" echo " Must validate and sanitize product data." CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Check for proper product query usage if grep -q "wp_get_post_terms.*product" "${file}"; then echo -e "${YELLOW} ⚠ Use wc_get_product_terms() instead of wp_get_post_terms() in ${file}${NC}" WARNING_COUNT=$((WARNING_COUNT + 1)) fi # Check for proper price handling (should use wc_get_price_including_tax/excluding_tax) if grep -qE "get_post_meta.*_price" "${file}" && ! grep -q "wc_get_price"; then echo -e "${YELLOW} ⚠ Use WC price functions in ${file}${NC}" echo " Prefer wc_get_price_including_tax() over direct meta access." WARNING_COUNT=$((WARNING_COUNT + 1)) fi # Check for proper action/filter prefixes if grep -qE "add_action.*'woocommerce_|add_filter.*'woocommerce_" "${file}"; then echo -e "${GREEN} ✓ Proper WooCommerce hook usage${NC}" fi # Check for Block-based checkout integration if grep -qE "woocommerce_store_api|CheckoutBlocks|StoreApi" "${file}"; then echo -e "${GREEN} ✓ Block-based checkout support detected${NC}" fi # Check for product attribute handling if grep -qE "get_the_terms.*product" "${file}" && grep -qE "pa_" "${file}"; then echo -e "${GREEN} ✓ Product attribute handling detected${NC}" fi # Check for tax handling if grep -qE "WC_Tax|calculate_tax|get_tax_rate" "${file}"; then echo -e "${GREEN} ✓ Tax calculation handling detected${NC}" fi done echo "" # ============================================ # 10. CLASS LOADING ERRORS & MISSING CLASSES # ============================================ echo -e "${MAGENTA}[10/10] Detecting Class Loading Errors and Missing Classes...${NC}" SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" CLASS_ERRORS=0 CLASS_WARNINGS=0 if command -v php >/dev/null 2>&1; then if [[ -f "${SCRIPT_DIR}/check-duplicate-classes.php" ]]; then php "${SCRIPT_DIR}/check-duplicate-classes.php" "${PLUGIN_DIR}" 2>&1 | while IFS= read -r line; do if [[ "$line" == *"MISSING CLASS"* ]]; then echo -e "${RED} ✗ CLASS LOADING ERROR: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"MISSING CLASS/TYPE"* ]]; then echo -e "${RED} ✗ CLASS LOADING ERROR: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"DUPLICATE CLASS"* ]]; then echo -e "${RED} ✗ CLASS LOADING ERROR: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"DUPLICATE INTERFACE"* ]]; then echo -e "${RED} ✗ CLASS LOADING ERROR: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"DUPLICATE TRAIT"* ]]; then echo -e "${RED} ✗ CLASS LOADING ERROR: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"install_class_not_loaded"* ]] || [[ "$line" == *"class_not_loaded"* ]]; then echo -e "${RED} ✗ CLASS LOADING ERROR: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"MISSING INCLUDE"* ]]; then echo -e "${RED} ✗ MISSING INCLUDE: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"ACTIVATION HOOK ISSUE"* ]]; then echo -e "${RED} ✗ ACTIVATION HOOK CLASS ERROR: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"UNDEFINED FUNCTION"* ]]; then echo -e "${RED} ✗ UNDEFINED FUNCTION: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"EARLY FUNCTION CALL"* ]]; then echo -e "${YELLOW} ⚠ CLASS LOADING WARNING: ${line}${NC}" CLASS_WARNINGS=$((CLASS_WARNINGS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) elif [[ "$line" == *"static_class_not_loaded"* ]]; then echo -e "${RED} ✗ STATIC CLASS NOT LOADED: ${line}${NC}" CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) elif [[ "$line" == *"Checking for"* ]] || [[ "$line" == *"No"* ]] || [[ "$line" == *"✓"* ]] || [[ "$line" == *"PASS"* ]] || [[ "$line" == *"FAIL"* ]] || [[ "$line" == *"Scanned"* ]] || [[ "$line" == *"found"* ]]; then : else echo " ${line}" fi done else echo -e "${YELLOW} ⚠ check-duplicate-classes.php not found - using static analysis${NC}" fi echo "" echo -e "${MAGENTA}[10.1/10] WooCommerce Class Loading Pattern Analysis...${NC}" for file in "${php_files[@]}"; do if grep -qE "(WC_[A-Z][a-zA-Z0-9_]*|woocommerce_[a-zA-Z0-9_]+)" "${file}"; then wc_classes=$(grep -oE "(WC_[A-Z][a-zA-Z0-9_]*|woocommerce_[a-zA-Z0-9_]+)" "${file}" | sort -u) for wc_class in $wc_classes; do if [[ "$wc_class" == "WC_"* ]] || [[ "$wc_class" == "woocommerce_"* ]]; then if ! grep -qE "(class|interface|trait)\s+${wc_class}" "${file}"; then if ! grep -qE "(function_exists|class_exists|interface_exists).*${wc_class}" "${file}"; then echo -e "${YELLOW} ⚠ WooCommerce Class Reference in ${file}:${NC}" echo " '${wc_class}' referenced but may require WooCommerce dependency check." CLASS_WARNINGS=$((CLASS_WARNINGS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi fi done fi if grep -qE "(class_exists|interface_exists|trait_exists)\s*\(" "${file}"; then if ! grep -qE "(class_exists|interface_exists|trait_exists)\s*\([^)]+\)\s*;" "${file}"; then echo -e "${YELLOW} ⚠ Dynamic Class Check in ${file}:${NC}" echo " Dynamic class loading detected - ensure WooCommerce dependency check." CLASS_WARNINGS=$((CLASS_WARNINGS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi if grep -qE "new\s+[A-Z][a-zA-Z0-9_]*\s*\(" "${file}"; then class_refs=$(grep -oE "new\s+[A-Z][a-zA-Z0-9_]*\s*\(" "${file}" | grep -oE "[A-Z][a-zA-Z0-9_]*" | sort -u) for class_ref in $class_refs; do if ! grep -qE "(class|interface|trait)\s+${class_ref}" "${file}"; then include_check=$(grep -nE "(require|include).*${class_ref}" "${file}" | head -1 || true) if [[ -z "$include_check" ]]; then if [[ "$class_ref" != "WC_"* ]] && [[ "$class_ref" != "WP_"* ]]; then echo -e "${RED} ✗ POTENTIAL MISSING CLASS in ${file}:${NC}" echo " Class '${class_ref}' instantiated but may not be loaded." CLASS_ERRORS=$((CLASS_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi fi done fi if grep -qE "[A-Z][a-zA-Z0-9_]*::[a-z]" "${file}"; then static_refs=$(grep -oE "[A-Z][a-zA-Z0-9_]*::[a-z][a-zA-Z0-9_]*" "${file}" | cut -d':' -f1 | sort -u) for static_ref in $static_refs; do if ! grep -qE "(class|interface|trait)\s+${static_ref}" "${file}"; then include_check=$(grep -nE "(require|include|use).*${static_ref}" "${file}" | head -1 || true) if [[ -z "$include_check" ]]; then if [[ "$static_ref" != "WC_"* ]] && [[ "$static_ref" != "WP_"* ]]; then echo -e "${YELLOW} ⚠ POTENTIAL MISSING STATIC CLASS in ${file}:${NC}" echo " Static class '${static_ref}' referenced but may not be loaded." CLASS_WARNINGS=$((CLASS_WARNINGS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi fi done fi if grep -qE "add_action\s*\(\s*['\"]woocommerce_" "${file}"; then if ! grep -qE "(class_exists|function_exists).*woocommerce" "${file}"; then echo -e "${YELLOW} ⚠ WooCommerce Hook Without Dependency Check in ${file}:${NC}" echo " WooCommerce hooks detected without dependency check." CLASS_WARNINGS=$((CLASS_WARNINGS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi if grep -qE "add_filter\s*\(\s*['\"]woocommerce_" "${file}"; then if ! grep -qE "(class_exists|function_exists).*woocommerce" "${file}"; then echo -e "${YELLOW} ⚠ WooCommerce Filter Without Dependency Check in ${file}:${NC}" echo " WooCommerce filters detected without dependency check." CLASS_WARNINGS=$((CLASS_WARNINGS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi done if [[ $CLASS_ERRORS -eq 0 ]] && [[ $CLASS_WARNINGS -eq 0 ]]; then echo -e "${GREEN} ✓ No class loading errors detected${NC}" elif [[ $CLASS_ERRORS -gt 0 ]]; then echo -e "${RED} ✗ ${CLASS_ERRORS} class loading error(s) found${NC}" else echo -e "${YELLOW} ⚠ ${CLASS_WARNINGS} class loading warning(s) found${NC}" fi else echo -e "${YELLOW} ⚠ PHP not available - skipping class loading validation${NC}" fi echo "" # ============================================ # 11. TOO FEW ARGUMENTS ERROR DETECTION # ============================================ echo -e "${MAGENTA}[11/11] Checking for Too Few Arguments Errors...${NC}" ARG_ERRORS=0 for file in "${php_files[@]}"; do # Check for array_key_exists with single argument (requires 2) if grep -nE "array_key_exists\s*\(\s*['\"][^'\"]+['\"]\s*\)" "${file}" >/dev/null 2>&1; then if ! grep -nE "array_key_exists\s*\(\s*['\"][^'\"]+['\"]\s*,\s*\\$" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "array_key_exists\s*\(\s*['\"][^'\"]+['\"]\s*\)" "${file}" | head -1) if [[ -n "$matches" ]] && ! grep -E "array_key_exists\s*\(\s*['\"][^'\"]+['\"]\s*,\s*[a-zA-Z_]" "${file}" >/dev/null 2>&1; then echo -e "${RED} ✗ TOO FEW ARGUMENTS in ${file}:${NC}" echo " array_key_exists() called with only 1 argument (requires 2)" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi fi # Check for function calls with missing required arguments # str_replace requires 3 arguments if grep -nE "str_replace\s*\(\s*[^)]+,\s*[^)]+\s*\)" "${file}" >/dev/null 2>&1; then if ! grep -E "str_replace\s*\(\s*[^)]+,\s*[^)]+,\s*[^)]+\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "str_replace\s*\(\s*[^)]+,\s*[^)]+\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${RED} ✗ TOO FEW ARGUMENTS in ${file}:${NC}" echo " str_replace() called with only 2 arguments (requires 3: search, replace, subject)" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi fi # Check for sprintf/printf with format but no arguments if grep -nE "(sprintf|printf)\s*\(\s*['\"][%].*['\"]\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "(sprintf|printf)\s*\(\s*['\"][%].*['\"]\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${YELLOW} ⚠ POSSIBLE TOO FEW ARGUMENTS in ${file}:${NC}" echo " sprintf/printf has format specifiers but may be missing arguments" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for array_slice with only array argument (requires at least 2) if grep -nE "array_slice\s*\(\s*\\\$[a-zA-Z_]+\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "array_slice\s*\(\s*\\\$[a-zA-Z_]+\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${RED} ✗ TOO FEW ARGUMENTS in ${file}:${NC}" echo " array_slice() called with only 1 argument (requires at least 2: array, offset)" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Check for in_array with only haystack argument (requires 2) if grep -nE "in_array\s*\(\s*\\\$[a-zA-Z_]+\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "in_array\s*\(\s*\\\$[a-zA-Z_]+\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${RED} ✗ TOO FEW ARGUMENTS in ${file}:${NC}" echo " in_array() called with only 1 argument (requires 2: needle, haystack)" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi # Check for explode with only delimiter argument (requires 2) if grep -nE "explode\s*\(\s*['\"][^'\"]+['\"]\s*\)" "${file}" >/dev/null 2>&1; then if ! grep -E "explode\s*\(\s*['\"][^'\"]+['\"]\s*,\s*\\$" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "explode\s*\(\s*['\"][^'\"]+['\"]\s*\)" "${file}" | head -1) if [[ -n "$matches" ]] && ! grep -E "explode\s*\(\s*['\"][^'\"]+['\"]\s*,\s*[^)]+\)" "${file}" >/dev/null 2>&1; then echo -e "${RED} ✗ TOO FEW ARGUMENTS in ${file}:${NC}" echo " explode() called with only 1 argument (requires 2: delimiter, string)" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) CRITICAL_COUNT=$((CRITICAL_COUNT + 1)) fi fi fi # Check for implode with only array argument (requires 1, but checking for wrong order) if grep -nE "implode\s*\(\s*\\\$[a-zA-Z_]+\s*,\s*['\"][^'\"]+['\"]\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "implode\s*\(\s*\\\$[a-zA-Z_]+\s*,\s*['\"][^'\"]+['\"]\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${YELLOW} ⚠ POSSIBLE ARGUMENT ORDER ERROR in ${file}:${NC}" echo " implode() arguments may be in wrong order (expected glue, array)" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for array_merge with single argument (unusual pattern) if grep -nE "array_merge\s*\(\s*\\\$[a-zA-Z_]+\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "array_merge\s*\(\s*\\\$[a-zA-Z_]+\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${YELLOW} ⚠ POSSIBLE TOO FEW ARGUMENTS in ${file}:${NC}" echo " array_merge() with single argument may indicate missing array parameter" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi # Check for WooCommerce-specific functions with missing arguments if grep -nE "wc_get_order\s*\(\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "wc_get_order\s*\(\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${YELLOW} ⚠ POSSIBLE TOO FEW ARGUMENTS in ${file}:${NC}" echo " wc_get_order() called without order ID argument" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi if grep -nE "wc_get_product\s*\(\s*\)" "${file}" >/dev/null 2>&1; then matches=$(grep -nE "wc_get_product\s*\(\s*\)" "${file}" | head -1) if [[ -n "$matches" ]]; then echo -e "${YELLOW} ⚠ POSSIBLE TOO FEW ARGUMENTS in ${file}:${NC}" echo " wc_get_product() called without product ID argument" echo "$matches" ARG_ERRORS=$((ARG_ERRORS + 1)) WARNING_COUNT=$((WARNING_COUNT + 1)) fi fi done if [[ $ARG_ERRORS -eq 0 ]]; then echo -e "${GREEN} ✓ No too few arguments errors detected${NC}" fi echo "" # ============================================ # 12. FINAL SUMMARY # ============================================ echo -e "${BLUE}========================================================${NC}" echo -e "${BLUE} VALIDATION SUMMARY ${NC}" echo -e "${BLUE}========================================================${NC}" if [[ "${CRITICAL_COUNT}" -gt 0 ]]; then echo -e "${RED}FAILED: ${CRITICAL_COUNT} critical issues found.${NC}" echo "This plugin is NOT safely compatible with modern WooCommerce." echo "Fix all Critical failures before usage." exit 1 elif [[ "${WARNING_COUNT}" -gt 0 ]]; then echo -e "${YELLOW}PASSED WITH WARNINGS: 0 critical, ${WARNING_COUNT} warnings.${NC}" echo "Review warnings for future-proofing." exit 0 else echo -e "${GREEN}PASSED: 0 critical, 0 warnings.${NC}" echo "Plugin appears fully compatible with strict standards." exit 0 fi