HEX
Server: nginx
System: Linux pool195-106-36.bur.atomicsites.net 6.12.57+deb12-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.57-1~bpo12+1 (2025-11-17) x86_64
User: (0)
PHP: 8.3.32
Disabled: pcntl_fork
Upload Files
File: /wordpress/mu-plugins/edge-cache/class-edge-cache-cli.php
<?php

/**
 * Edge Cache control - query and control the Edge Cache.
 *
 * The Edge Cache improves the performance of your site, allowing pages to run faster and reducing
 * the load on your server. These commands allow you to check and update the status of the Edge
 * Cache for your site.
 */

class Edge_Cache_CLI extends WP_CLI_Command {
	private const STATUS_STRINGS = [
		Edge_Cache_Plugin::EC_ENABLED  => '%GEnabled%n',
		Edge_Cache_Plugin::EC_DISABLED => '%rDisabled%n',
		Edge_Cache_Plugin::EC_DDOS     => '%YDDOS mode%n',
	];

	/**
	 * Display the current status of the Edge Cache for this site.
	 */
	public function status() {
		$edge_cache = Edge_Cache_Plugin::get_instance();

		$status = $edge_cache->get_ec_status();
		if ( ! isset( self::STATUS_STRINGS[ $status ] ) ) {
			WP_CLI::error( 'Failed to query edge cache status.' );
		}

		$ddos_until = $edge_cache->get_ec_ddos_until();

		$this->success_status( $status );
		if ( $ddos_until < 0 || $ddos_until > time() ) {
			WP_CLI::success(
				$this->format_defensive_mode_message( $ddos_until )
			);
		}
	}

	/**
	 * Format a message about Defensive mode for display.
	 */
	private function format_defensive_mode_message( $timestamp ) {
		if ( $timestamp === -1 ) {
			return WP_CLI::colorize( "Defensive mode enabled %Gindefinitely%n." );
		} else {
			return WP_CLI::colorize(
				"Defensive mode enabled until %G" . date( 'Y-m-d H:i:s', $timestamp ) . "%n"
			);
		}
	}

	/**
	 * Purge the Edge Cache - either for the whole site, or for a URI.
	 * 
	 * You must specify one of '--url' or '--domain' when purging.
	 *
	 * ## OPTIONS
	 * [<urls>...]
	 * : URLs to purge.
	 * 
	 * [--domain]
	 * : Purge the whole domain.
	 *
	 * [--yes]
	 * : Answer yes to the confirmation prompt (needed when purging with --domain).
	 *
	 * ## EXAMPLES
	 * 
	 * 	wp edge-cache purge "https://my-site.com/path" "https://my-site.com/path?get=param"
	 * 	wp edge-cache purge --domain
	 *
	 *  @param array $args Positional arguments (URLs).
	 */
	public function purge( $urls, $assoc_args ) {
		$purge_domain = ! empty( $assoc_args['domain'] );

		// Require either URLs or a --domain flag.
		if ( empty( $urls ) && ! $purge_domain ) {
			WP_CLI::Error( 'You must either specify URLs to purge, or specify --domain to purge the whole domain.' );
		}

		if ( ! empty( $urls ) && $purge_domain ) {
			WP_CLI::Error( 'You may only either specify URLs to purge, or specify --domain to purge the whole domain.' );
		}

		// Only accept urls which are for the correct domain.
		$self_domain = parse_url( get_site_url(), PHP_URL_HOST );
		foreach ( $urls as $url ) {
			$target_domain = parse_url( $url, PHP_URL_HOST );
			if ( $target_domain !== $self_domain ) {
				WP_CLI::Error( 'You can only purge URLs for ' . $self_domain );
			}
		}

		// Always verify before purging a whole domain.
		if ( $purge_domain ) {
			WP_CLI::warning( WP_CLI::colorize( "You are about to purge %Rall edge-cached content%n for your entire site." ) );
			WP_CLI::confirm( "Are you sure you want to proceed?", $assoc_args );
		}

		// Actually do the purge.
		$plugin = Edge_Cache_Plugin::get_instance();
		if ( $purge_domain ) {
			$result = $plugin->purge_domain_now( 'cli' );
		} else {
			$result = $plugin->purge_uris_now( $urls, 'cli' );
		}

		// Report errors.
		if ( ! $result ) {
			if ( $plugin->has_hit_purge_ratelimit() ) {
				WP_CLI::Error( 'Your site has purged too many times recently. Please try again in a few minutes.' );
			} else {
				$error = $plugin->get_last_ec_error() ?? 'uknown error';

				WP_CLI::Error( sprintf( 'Purge request failed: %s. Please try again in a few minutes.', $error ) );
			}
		}

		WP_CLI::success( 'Purge successful.' );
	}

	/**
	 * Set the Edge Cache into Defensive mode.
	 * 
	 * Enable Defensive mode when your site is receiving more traffic than it can handle. When
	 * it's enabled, visitors to your site will have their browsers tested using a proof-of-work
	 * check before they are allowed to access your site.
	 *
	 * ## OPTIONS
	 * [--time=<time>]
	 * : Specify a number of minutes (m) or hours (h) to engage Defensive mode for. Defaults to 1h.
	 * 
	 * [--end]
	 * : Pass this argument to turn off Defensive mode and return to normal serivce.
	 *
	 * [--indefinitely]
	 * : Pass this argument to enable Defensive mode indefinitely. Warning: defensive mode is intended
	 * to be a temporary measure, and leaving it on indefinitely may lead to a poor experience for your visitors.
	 *
	 * [--yes]
	 * : Answer yes to the confirmation prompt (needed when using --indefinitely).
	 *
	 * ## Examples
	 *      wp edge-cache defensive-mode --time=5h
	 *
	 * @subcommand defensive-mode
	 */
	public function defensive_mode( $args, $assoc_args ) {
		$has_end = isset( $assoc_args['end'] );
		$has_time = isset( $assoc_args['time'] );
		$has_indefinitely = isset( $assoc_args['indefinitely'] );

		if ( $has_end && $has_time ) {
			WP_CLI::Error( 'You can only specify either --end or --time.' );
		}

		// Defensive mode is not available when "old school" DDOS mode is active.
		$status = Edge_Cache_Plugin::get_instance()->get_ec_status();
		if ( $status === Edge_Cache_Plugin::EC_DDOS ) {
			WP_CLI::Error(
				WP_CLI::colorize(
					'Defensive mode cannot be enabled in status ' .
					self::STATUS_STRINGS[ $status ] 
				)
			);
		}

		if ( $has_end ) {
			$timestamp = 0;
		} else if ( $has_indefinitely ) {
			$timestamp = -1;

			WP_CLI::warning( WP_CLI::colorize( "You are about to enable defensive mode %Rindefinitely%n." ) );
			WP_CLI::line( "This will cause all visitors to your site to pass a browser-check before they are allowed to access your site." );
			WP_CLI::confirm( "Are you sure you want to proceed?", $assoc_args );
		} else {
			$time = '1h';
			if ( ! empty( $assoc_args['time'] ) ) {
				$time = $assoc_args['time'];
			}

			$unit = substr( $time, -1 );
			$multiplier = 60; // default to minutes.
			if ( 'h' === $unit ) {
				$multiplier = 60 * 60;
			} else if ( 'd' === $unit ) {
				$multiplier = 24 * 60 * 60;
			}

			$seconds = intval( $time ) * $multiplier;

			if ( $seconds < 30 * 60 ) {
				WP_CLI::error( 'Time limit cannot be less than 30 minutes.' );
			}

			if ( $seconds > 7 * 24 * 60 * 60 ) {
				WP_CLI::error( 'Time limit cannot be greater than 1 week.' );
			}

			$timestamp = time() + $seconds;
		}

		$result = $this->run_ec_command( 'ddos_until', [ 'timestamp' => $timestamp ] ); 
		if ( is_wp_error( $result ) ) {
			WP_CLI::error( $result->get_error_message() );
		}

		if ( $timestamp === 0 ) {
			WP_CLI::success( 'Defensive mode disabled.' );
		} else {
			WP_CLI::success(
				$this->format_defensive_mode_message( $timestamp )
			);
		}
	}

	/**
	 * Turn on the edge cache.
	 */
	public function enable() {
		$result = $this->run_ec_command( 'on' );
		if ( is_wp_error( $result ) ) {
			WP_CLI::error( $result->get_error_message() );
		}

		$this->success_status( Edge_Cache_Plugin::EC_ENABLED );
	}

	/**
	 * Turn off the edge cache.
	 */
	public function disable() {
		$result = $this->run_ec_command( 'off' );
		if ( is_wp_error( $result ) ) {
			WP_CLI::error( $result->get_error_message() );
		}

		$this->success_status( Edge_Cache_Plugin::EC_DISABLED );
	}

	private function success_status( $status ) {
		WP_CLI::success(
			WP_CLI::colorize( 'Edge cache status: ' . self::STATUS_STRINGS[ $status ] )
		);
	}

	private function run_ec_command( $command, $args = [] ) {
		$ec_plugin = Edge_Cache_Plugin::get_instance();

		$body = [
			'wp_action' => 'manual_cli_set',
			'wp_domain' => parse_url( get_site_url(), PHP_URL_HOST ),
			'at_host'   => php_uname( 'n' ),
		];

		$body = array_merge( $body, $args );

		$response = $ec_plugin->query_ec_backend( $command, array(
			'timeout' => 30,
			'body'    => $body
		) );
		if ( $response['success'] === false ) {
			$error = isset( $response['error'] ) ? $response['error'] : 'An error occurred while processing your request.';
			return new WP_Error( 'request_failed', $error );
		}

		return true;
	}

	/**
	 * Maintain backwards compatibility without polluting the documentation: register
	 * defensive_mode as a subcommand if being invoked, as a wrapper for defensive-mode.
	 */
	public static function register_backwards_compatible_commands() {
		$arguments = WP_CLI::get_runner()->arguments;
		if ( ! in_array( 'defensive_mode', $arguments, true ) ) {
			return;
		}

		$handler = function ( $args, $assoc_args ) {
			$edge_cache_cli = new Edge_Cache_CLI();
			$edge_cache_cli->defensive_mode( $args, $assoc_args );
		};

		WP_CLI::add_command(
			'edge-cache defensive_mode',
			$handler,
			[
				'shortdesc' => 'Deprecated; please use defensive-mode instead.',
			]
		);
	}
}

WP_CLI::add_command('edge-cache', 'Edge_Cache_CLI');
Edge_Cache_CLI::register_backwards_compatible_commands();