Skip to content
wiki.fftac.org

Linkreviewscanner - Source Excerpt 01

Back to Linkreviewscanner

Summary

This source excerpt preserves a bounded section of Antichrist.net/wp-content/plugins/uaix-locale-router/src/Diagnostics/LinkReviewScanner.php so readers can inspect the evidence without opening the full source file.

**Source path:** Antichrist.net/wp-content/plugins/uaix-locale-router/src/Diagnostics/LinkReviewScanner.php

<?php

namespace UAIXLocaleRouter\Diagnostics;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

final class LinkReviewScanner {
	/**
	 * Review active UAIX theme and plugin files for hardcoded locale-link patterns.
	 *
	 * @param bool $force_refresh Whether to bypass the transient cache.
	 * @return array
	 */
	public static function locale_link_review_diagnostics( $force_refresh = false ) {
		if ( ! $force_refresh ) {
			$cached = get_transient( 'uaixlr_link_review' );

			if ( is_array( $cached ) ) {
				return $cached;
			}
		}

		$roots    = self::link_review_scope();
		$findings = array();
		$counts   = array(
			'bare_internal_links'   => 0,
			'explicit_locale_links' => 0,
			'files_scanned'         => 0,
		);

		foreach ( $roots as $root ) {
			self::scan_link_review_root( $root, $findings, $counts );
		}

		$severity = 'good';
		$summary  = __( 'No hardcoded locale-link issues were detected in the active UAIX theme and plugin sources.', 'uaix-locale-router' );
		$issues   = array();

		if ( $counts['bare_internal_links'] > 0 ) {
			$severity = 'warning';
			$issues[] = sprintf(
				/* translators: %d: finding count */
				__( 'Found %d root-relative internal link(s) such as href="/spec/" or href="/". These rely on redirect fallback and do not preserve the active locale context.', 'uaix-locale-router' ),
				$counts['bare_internal_links']
			);
		}

		if ( $counts['explicit_locale_links'] > 0 ) {
			$severity = 'warning';
			$issues[] = sprintf(
				/* translators: %d: finding count */
				__( 'Found %d explicit locale-pinned link(s) such as /en-us/... . These should be limited to intentional language switchers or documented exceptions.', 'uaix-locale-router' ),
				$counts['explicit_locale_links']
			);
		}

		if ( ! empty( $issues ) ) {
			$summary = __( 'Review locale-link findings before publishing. Bare internal links should be locale-aware, and explicit locale URLs should only exist when they intentionally pin one language.', 'uaix-locale-router' );
		}

		$result = array(
			'severity'                => $severity,
			'summary'                 => $summary,
			'issues'                  => $issues,
			'findings'                => array_slice( $findings, 0, 12 ),
			'bare_internal_links'     => $counts['bare_internal_links'],
			'explicit_locale_links'   => $counts['explicit_locale_links'],
			'files_scanned'           => $counts['files_scanned'],
			'roots'                   => $roots,
			'guidance'                => array(
				__( 'Use WordPress or locale-aware helpers for internal navigation instead of bare root-relative links such as href="/spec/" or href="/".', 'uaix-locale-router' ),
				__( 'Reserve explicit locale-prefixed links such as /en-us/... for intentional language switchers or similarly explicit language-pinning flows.', 'uaix-locale-router' ),
				__( 'Bare non-locale URLs are acceptable for external entry points, but normal internal navigation should preserve the active locale instead of forcing a redirect hop.', 'uaix-locale-router' ),
				__( 'If a finding is intentionally exempt, add a nearby uaixlr-review-ignore marker and document why before publishing.', 'uaix-locale-router' ),
			),
			'checked_at'              => time(),
		);

		set_transient( 'uaixlr_link_review', $result, 15 * MINUTE_IN_SECONDS );

		return $result;
	}

	/**
	 * Build the review scope for active UAIX-controlled theme and plugin code.
	 *
	 * @return array
	 */
	private static function link_review_scope() {
		$roots           = array();
		$stylesheet_dir  = function_exists( 'get_stylesheet_directory' ) ? get_stylesheet_directory() : '';
		$template_dir    = function_exists( 'get_template_directory' ) ? get_template_directory() : '';
		$plugin_roots    = array( 'uaix-locale-router', 'uaix-core', 'uaix-bridge', 'uaix-modules' );

		if ( $stylesheet_dir && is_dir( $stylesheet_dir ) ) {
			$roots[ $stylesheet_dir ] = array(
				'label' => __( 'Active theme', 'uaix-locale-router' ),
				'path'  => $stylesheet_dir,
			);
		}

		if ( $template_dir && $template_dir !== $stylesheet_dir && is_dir( $template_dir ) ) {
			$roots[ $template_dir ] = array(
				'label' => __( 'Parent theme', 'uaix-locale-router' ),
				'path'  => $template_dir,
			);
		}

		foreach ( $plugin_roots as $plugin_root ) {
			$path = trailingslashit( WP_PLUGIN_DIR ) . $plugin_root;

			if ( is_dir( $path ) ) {
				$roots[ $path ] = array(
					'label' => sprintf(
						/* translators: %s: plugin slug */
						__( 'Plugin: %s', 'uaix-locale-router' ),
						$plugin_root
					),
					'path'  => $path,
				);
			}
		}

		return array_values( $roots );
	}

	/**
	 * Scan one source root for locale-link review findings.
	 *
	 * @param array $root Root metadata.
	 * @param array $findings Findings accumulator.
	 * @param array $counts Counts accumulator.
	 * @return void
	 */
	private static function scan_link_review_root( array $root, array &$findings, array &$counts ) {
		if ( empty( $root['path'] ) || ! is_dir( $root['path'] ) ) {
			return;
		}

		$max_files = (int) apply_filters( 'uaixlr_link_review_max_files', 500 );
		$max_bytes = (int) apply_filters( 'uaixlr_link_review_max_file_bytes', 262144 );
		$max_files = $max_files > 0 ? $max_files : 500;
		$max_bytes = $max_bytes > 0 ? $max_bytes : 262144;

		try {
			$iterator = new \RecursiveIteratorIterator(
				new \RecursiveDirectoryIterator(
					$root['path'],
					\FilesystemIterator::SKIP_DOTS
				)
			);
		} catch ( \UnexpectedValueException $exception ) {
			return;
		}

		foreach ( $iterator as $file_info ) {
			if ( $counts['files_scanned'] >= $max_files ) {
				return;
			}

			if ( ! $file_info instanceof \SplFileInfo || ! $file_info->isFile() ) {
				continue;
			}

			$file_path = $file_info->getPathname();

			if ( $file_info->getSize() > $max_bytes || self::should_skip_link_review_path( $file_path ) || ! self::is_link_review_file( $file_path ) ) {
				continue;
			}

			++$counts['files_scanned'];
			self::scan_link_review_file( $file_path, $root['label'], $findings, $counts );
		}
	}

	/**
	 * Scan a single file for locale-link review findings.
	 *
	 * @param string $file_path File path.
	 * @param string $root_label Human-readable source label.
	 * @param array  $findings Findings accumulator.
	 * @param array  $counts Counts accumulator.
	 * @return void
	 */
	private static function scan_link_review_file( $file_path, $root_label, array &$findings, array &$counts ) {
		$lines = @file( $file_path );

		if ( false === $lines ) {
			return;
		}

		$bare_internal_pattern = '~<[^>]*\b(?:href|action)\s*=\s*(["\'])/(?!/|#|wp-admin/|wp-login\.php|wp-json/|wp-content/|wp-includes/|favicon\.ico|robots\.txt|xmlrpc\.php|feed(?:["\'/]|$)|sitemap(?:["\'/.\-]|$)|[A-Za-z]{2,8}(?:-[A-Za-z0-9]{2,8})+/)([^"\']*)\1~i';
		$explicit_locale_pattern = '~<[^>]*\b(?:href|action)\s*=\s*(["\'])/([A-Za-z]{2,8}(?:-[A-Za-z0-9]{2,8})+)(?:/[^"\']*)?\1~';

		foreach ( $lines as $index => $line ) {
			if ( false !== strpos( $line, 'uaixlr-review-ignore' ) ) {
				continue;
			}

			if ( preg_match( $bare_internal_pattern, $line ) ) {
				++$counts['bare_internal_links'];

				if ( count( $findings ) < 12 ) {
					$findings[] = array(
						'type'    => 'bare_internal_link',
						'label'   => __( 'Bare internal link', 'uaix-locale-router' ),
						'source'  => $root_label,
						'path'    => self::relative_review_path( $file_path ),
						'line'    => $index + 1,
						'snippet' => trim( $line ),
					);
				}
			}

			if ( preg_match( $explicit_locale_pattern, $line ) ) {
				++$counts['explicit_locale_links'];

				if ( count( $findings ) < 12 ) {
					$findings[] = array(
						'type'    => 'explicit_locale_link',
						'label'   => __( 'Explicit locale link', 'uaix-locale-router' ),
						'source'  => $root_label,
						'path'    => self::relative_review_path( $file_path ),
						'line'    => $index + 1,
						'snippet' => trim( $line ),
					);
				}
			}
		}
	}

	/**
	 * Determine whether a file should be included in locale-link review.
	 *
	 * @param string $file_path File path.
	 * @return bool
	 */
	private static function is_link_review_file( $file_path ) {
		return in_array( strtolower( (string) pathinfo( $file_path, PATHINFO_EXTENSION ) ), array( 'php', 'html', 'js' ), true );
	}

	/**
	 * Determine whether a path should be excluded from locale-link review.
	 *
	 * @param string $file_path File path.
	 * @return bool
	 */
	private static function should_skip_link_review_path( $file_path ) {
		$normalized = str_replace( '\\', '/', (string) $file_path );
		$skip_parts = array(
			'/vendor/',
			'/node_modules/',
			'/languages/',
			'/data/',
			'/docs/',
			'/tests/',
		);

		foreach ( $skip_parts as $skip_part ) {
			if ( false !== strpos( $normalized, $skip_part ) ) {