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/plugins/wp-cloud-client/1.2.0/src/Handler/BootstrapSiteHandler.php
<?php

declare(strict_types=1);

namespace VPlugins\WPCloudClient\Handler;

use VPlugins\WPCloudClient\SiteOptions\OptionRegistry;

final class BootstrapSiteHandler extends AbstractHandler {

	private const OPTION_PARAMS = [ 'site_title', 'site_tagline', 'admin_email', 'timezone' ];

	private const LEGACY_SSO_OPTIONS = [
		'wsp_custom_single_signon_url',
		'wsp_custom_single_signout_url',
	];

	/**
	 * Constructor.
	 *
	 * @param OptionRegistry $optionRegistry The site option registry used to write bootstrap fields.
	 */
	public function __construct(
		private readonly OptionRegistry $optionRegistry,
	) {}

	/**
	 * Return the action name this handler responds to.
	 *
	 * @return string The action identifier.
	 */
	public function action(): string {
		return 'bootstrap_site';
	}

	/**
	 * Apply post-create site bootstrap: option updates, legacy SSO option cleanup,
	 * optional user purge. Re-runnable — none of the steps are gated to first-create.
	 *
	 * @param array<string, mixed> $params Bootstrap parameters. Accepted keys: site_title, site_tagline, admin_email, timezone (any subset; only supplied keys are written), and keep_user_login (string, optional — when set, all users except this login are deleted and their posts reassigned to the kept user).
	 * @return array{options_applied: array<string, mixed>, sso_options_removed: list<string>, users_purged: int} Summary of applied options, removed SSO options, and purged user count.
	 */
	public function execute( array $params ): array {
		$optionUpdates = [];
		foreach ( self::OPTION_PARAMS as $key ) {
			if ( array_key_exists( $key, $params ) ) {
				$optionUpdates[ $key ] = $params[ $key ];
			}
		}

		$applied = [] === $optionUpdates ? [] : $this->optionRegistry->write( $optionUpdates );

		$removed = [];
		foreach ( self::LEGACY_SSO_OPTIONS as $option ) {
			if ( delete_option( $option ) ) {
				$removed[] = $option;
			}
		}

		$purged = 0;
		if ( isset( $params['keep_user_login'] ) && '' !== $params['keep_user_login'] ) {
			$purged = $this->purgeUsers( (string) $params['keep_user_login'] );
		}

		return [
			'options_applied'     => $applied,
			'sso_options_removed' => $removed,
			'users_purged'        => $purged,
		];
	}

	/**
	 * Delete all users except the one matching $keepLogin, reassigning their posts to the kept user.
	 *
	 * @param string $keepLogin WordPress login of the user to retain.
	 * @return int Number of users that were deleted.
	 *
	 * @throws \InvalidArgumentException When $keepLogin does not resolve to an existing user.
	 */
	private function purgeUsers( string $keepLogin ): int {
		if ( ! function_exists( 'wp_delete_user' ) ) {
			require_once ABSPATH . 'wp-admin/includes/user.php';
		}

		$keepUser = get_user_by( 'login', $keepLogin );
		if ( false === $keepUser ) {
			throw new \InvalidArgumentException(
				sprintf( 'keep_user_login user "%s" not found', $keepLogin ),
			);
		}

		$purged  = 0;
		$keepId  = (int) $keepUser->ID;
		$victims = get_users(
			[
				'fields' => [ 'ID' ],
				'number' => -1,
			],
		);

		foreach ( $victims as $user ) {
			$userId = (int) $user->ID;
			if ( $userId === $keepId ) {
				continue;
			}

			if ( wp_delete_user( $userId, $keepId ) ) {
				++$purged;
			}
		}

		return $purged;
	}
}