File: /wordpress/plugins/wp-cloud-client/previous/src/Handler/TriggerThemeUpdateHandler.php
<?php
declare(strict_types=1);
namespace VPlugins\WPCloudClient\Handler;
use VPlugins\WPCloudClient\Support\Logger;
use VPlugins\WPCloudClient\Support\RollbackManager;
/**
* Triggers an immediate theme update or rollback via the REST actions endpoint.
*
* Action name: trigger_theme_update
*
* Required params:
* - slug (string) - Theme stylesheet to update (e.g., "twentytwentyfour").
*
* Optional params:
* - rollback (bool) – Revert to the version recorded before the last update.
*
* Update response example:
* {
* "updated": true,
* "slug": "twentytwentyfive",
* "previous_version": "1.0",
* "new_version": "1.1",
* "message": "Theme updated from 1.0 to 1.1."
* }
*
* Rollback response example:
* {
* "rolled_back": true,
* "slug": "twentytwentyfive",
* "previous_version": "1.1",
* "restored_version": "1.0",
* "message": "Theme rolled back from 1.1 to 1.0."
* }
*/
final class TriggerThemeUpdateHandler extends AbstractHandler {
/**
* Class constructor.
*
* @param RollbackManager $rollbackManager Rollback manager instance.
* @param Logger $logger Logger instance.
*/
public function __construct(
private readonly RollbackManager $rollbackManager,
private readonly Logger $logger,
) {}
/**
* Return the action name.
*
* @return string
*/
public function action(): string {
return 'trigger_theme_update';
}
/**
* Trigger a theme update or rollback.
*
* @param array<string, mixed> $params Action parameters.
* @return array<string, mixed> Operation result.
*
* @throws \InvalidArgumentException When required params are missing.
* @throws \RuntimeException On failure.
*/
public function execute( array $params ): array {
$this->requireParams( $params, 'slug' );
$slug = (string) $params['slug'];
$rollback = ! empty( $params['rollback'] );
$this->validateThemeExists( $slug );
$this->loadUpgraderDependencies();
if ( $rollback ) {
return $this->performRollback( $slug );
}
return $this->performUpdate( $slug );
}
/**
* Validate that the theme exists.
*
* @param string $slug Theme slug.
* @throws \InvalidArgumentException If theme not found.
*/
private function validateThemeExists( string $slug ): void {
$theme = wp_get_theme( $slug );
if ( ! $theme->exists() ) {
throw new \InvalidArgumentException( sprintf( 'Theme "%s" not found.', $slug ) );
}
}
/**
* Download and install the latest version of a theme.
*
* @param string $slug Theme slug.
* @return array<string, mixed>
* @throws \RuntimeException When the upgrade fails.
*/
private function performUpdate( string $slug ): array {
$theme = wp_get_theme( $slug );
$currentVersion = $theme->get( 'Version' );
// Check if update is available.
$transient = get_site_transient( 'update_themes' );
if ( ! is_object( $transient ) || ! isset( $transient->response[ $slug ] ) ) {
return [
'updated' => false,
'slug' => $slug,
'message' => 'No update available for this theme.',
];
}
$updateData = $transient->response[ $slug ];
$newVersion = $updateData['new_version'] ?? 'unknown';
// Construct download URL for current version (for rollback).
$downloadUrl = sprintf(
'https://downloads.wordpress.org/theme/%s.%s.zip',
$slug,
$currentVersion
);
// Store current version + download URL for rollback.
$this->rollbackManager->recordPreUpdateVersion( 'theme', $slug, $currentVersion, $downloadUrl );
// Perform the update.
$upgrader = new \Theme_Upgrader( new \Automatic_Upgrader_Skin() );
$result = $upgrader->upgrade( $slug );
if ( is_wp_error( $result ) ) {
$this->logger->error( 'Theme update failed', $result->get_error_message() );
throw new \RuntimeException(
sprintf( 'Update failed for theme "%s": %s', $slug, $result->get_error_message() )
);
}
$this->logger->info(
'Theme updated',
[
'slug' => $slug,
'from' => $currentVersion,
'to' => $newVersion,
]
);
return [
'updated' => true,
'slug' => $slug,
'previous_version' => $currentVersion,
'new_version' => $newVersion,
'message' => sprintf(
'Theme updated from %s to %s.',
$currentVersion,
$newVersion
),
];
}
/**
* Re-install the version that was active before the last update.
*
* @param string $slug Theme slug.
* @return array<string, mixed>
* @throws \RuntimeException When no rollback target exists or the install fails.
*/
private function performRollback( string $slug ): array {
if ( ! $this->rollbackManager->canRollback( 'theme', $slug ) ) {
throw new \RuntimeException(
sprintf( 'No rollback target is recorded for theme "%s". Trigger an update first.', $slug )
);
}
$currentVersion = $this->getCurrentVersion( $slug );
$targetVersion = $this->rollbackManager->getPreviousVersion( 'theme', $slug );
$downloadUrl = $this->rollbackManager->getDownloadUrl( 'theme', $slug );
if ( null === $downloadUrl || '' === $downloadUrl ) {
throw new \RuntimeException(
sprintf( 'No rollback download URL recorded for theme "%s". Trigger an update first.', $slug )
);
}
$upgrader = new \Theme_Upgrader( new \Automatic_Upgrader_Skin() );
$result = $upgrader->install( $downloadUrl, [ 'overwrite_package' => true ] );
if ( is_wp_error( $result ) ) {
$this->logger->error( 'Theme rollback failed', $result->get_error_message() );
throw new \RuntimeException(
sprintf( 'Rollback failed for theme "%s": %s', $slug, $result->get_error_message() )
);
}
$this->rollbackManager->clearPreviousVersion( 'theme', $slug );
$this->logger->info(
'Theme rolled back',
[
'slug' => $slug,
'from' => $currentVersion,
'to' => $targetVersion,
]
);
return [
'rolled_back' => true,
'slug' => $slug,
'previous_version' => $currentVersion,
'restored_version' => $targetVersion,
'message' => sprintf(
'Theme rolled back from %s to %s.',
$currentVersion,
$targetVersion
),
];
}
/**
* Get the current version of a theme.
*
* @param string $slug Theme slug.
* @return string Current version.
*/
private function getCurrentVersion( string $slug ): string {
$theme = wp_get_theme( $slug );
return $theme->get( 'Version' );
}
/**
* Include required WordPress upgrader classes if not already loaded.
*
* @return void
*/
private function loadUpgraderDependencies(): void {
if ( ! function_exists( 'request_filesystem_credentials' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
if ( ! class_exists( \WP_Upgrader::class ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php';
}
if ( ! class_exists( \Automatic_Upgrader_Skin::class ) ) {
require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader-skins.php';
}
}
}