<?php
/**
 * Detection of a physical robots.txt file in the web root.
 *
 * If such a file exists the web server serves it directly and WordPress is never
 * involved: do_robots() only runs on a WP-routed request (wp-includes/template-loader.php).
 * Our robots_txt filter then does nothing, tam_rank_robots_txt does nothing, and the
 * settings screen shows content that is not live.
 *
 * Yoast users are the most likely group to have one, because Yoast's own File editor
 * tool creates it - and Yoast users are the main migration audience.
 *
 * TamRank never deletes the file and never offers to. Same line as the rest of the
 * plugin: we do not remove things the user put there. We detect it, import what is in
 * it, and say plainly that the file keeps winning until the user removes it.
 */

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

class TamRank_Physical_Robots {

    /**
     * Candidate paths for a physical robots.txt.
     *
     * Both are checked, not one or the other. Yoast's own variant only falls back to
     * DOCUMENT_ROOT when get_home_path() is not writable - that is a write fallback, and
     * for read-only detection it would miss a file sitting in DOCUMENT_ROOT whenever the
     * home path happens to be writable.
     *
     * get_home_path() lives in wp-admin/includes/file.php and is not loaded on the front
     * end, hence the function_exists guard (same pattern as the .htaccess editor).
     *
     * @return string[] Absolute file paths, de-duplicated.
     */
    public static function candidate_paths() {
        $paths = array();

        if ( ! function_exists( 'get_home_path' ) && defined( 'ABSPATH' ) && file_exists( ABSPATH . 'wp-admin/includes/file.php' ) ) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }

        if ( function_exists( 'get_home_path' ) ) {
            $home = get_home_path();
            if ( is_string( $home ) && '' !== $home ) {
                $paths[] = trailingslashit( $home ) . 'robots.txt';
            }
        }

        if ( ! empty( $_SERVER['DOCUMENT_ROOT'] ) ) {
            $doc_root = sanitize_text_field( wp_unslash( $_SERVER['DOCUMENT_ROOT'] ) );
            if ( '' !== $doc_root ) {
                $paths[] = trailingslashit( $doc_root ) . 'robots.txt';
            }
        }

        if ( defined( 'ABSPATH' ) ) {
            $paths[] = trailingslashit( ABSPATH ) . 'robots.txt';
        }

        return array_values( array_unique( $paths ) );
    }

    /**
     * Path of the physical robots.txt, or '' when there is none.
     *
     * @return string
     */
    public static function find() {
        foreach ( self::candidate_paths() as $path ) {
            if ( @file_exists( $path ) && @is_file( $path ) ) { // phpcs:ignore WordPress.PHP.NoSilencedErrors.Discouraged -- open_basedir/permissions must not fatal a read-only probe
                return $path;
            }
        }

        return '';
    }

    /**
     * Whether a physical robots.txt exists.
     *
     * @return bool
     */
    public static function exists() {
        return '' !== self::find();
    }

    /**
     * Contents of the physical robots.txt, or '' when unreadable or absent.
     *
     * @return string
     */
    public static function read() {
        $path = self::find();
        if ( '' === $path ) {
            return '';
        }

        // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents, WordPress.PHP.NoSilencedErrors.Discouraged -- local file read; WP_Filesystem needs credentials we do not have here and a failed read must degrade to ''
        $contents = @file_get_contents( $path );

        return is_string( $contents ) ? str_replace( array( "\r\n", "\r" ), "\n", $contents ) : '';
    }

}
