ACF

Updater{}ACF 1.0

class for handling API services.

Хуков нет.

Использование

$Updater = new Updater();
// use class methods

Методы

  1. public __construct()
  2. public add_plugin( $plugin )
  3. public get_expiration( $response = false, $min = 10800, $max = 604800 )
  4. public get_no_update( $basename = '', $force_check = false )
  5. public get_plugin_by( $key = '', $value = null )
  6. public get_plugin_info( $id = '', $force_check = false )
  7. public get_plugin_update( $basename = '', $force_check = false )
  8. public get_plugin_updates( $force_check = false )
  9. public modify_plugin_details( $result, $action = null, $args = null )
  10. public modify_plugins_transient( $transient )
  11. public refresh_plugins_transient()
  12. public request( $endpoint = '', $body = null )

Код Updater{} ACF 6.4.2

class Updater {

	/**
	 * The Updater version
	 *
	 * @var string
	 */
	public $version = '3.0';

	/**
	 * The array of registered plugins
	 *
	 * @var array
	 */
	public $plugins = array();

	/**
	 * Counts the number of plugin update checks
	 *
	 * @var integer
	 */
	public $checked = 0;

	/**
	 * Sets up the class functionality.
	 *
	 * @since   5.0.0
	 */
	public function __construct() {

		// disable showing PRO updates if show updates is hidden.
		if ( acf_is_pro() && ! acf_pro_is_updates_page_visible() ) {
			return;
		}

		// append update information to transient.
		add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'modify_plugins_transient' ), 10, 1 );

		// modify plugin data visible in the 'View details' popup.
		add_filter( 'plugins_api', array( $this, 'modify_plugin_details' ), 10, 3 );
	}

	/**
	 * Registeres a plugin for updates.
	 *
	 * @since   5.5.10
	 *
	 * @param   array $plugin The plugin array.
	 * @return  void
	 */
	public function add_plugin( $plugin ) {

		// validate.
		$plugin = wp_parse_args(
			$plugin,
			array(
				'id'       => '',
				'key'      => '',
				'slug'     => '',
				'basename' => '',
				'version'  => '',
			)
		);

		// Check if is_plugin_active() function exists. This is required on the front end of the
		// site, since it is in a file that is normally only loaded in the admin.
		if ( ! function_exists( 'is_plugin_active' ) ) {
			require_once ABSPATH . 'wp-admin/includes/plugin.php';
		}

		// add if is active plugin (not included in theme).
		if ( is_plugin_active( $plugin['basename'] ) ) {
			$this->plugins[ $plugin['basename'] ] = $plugin;
		}
	}

	/**
	 * Returns a registered plugin for the give key and value.
	 *
	 * @since   5.7.2
	 *
	 * @param   string $key   The array key to compare.
	 * @param   string $value The value to compare against.
	 * @return  array|false
	 */
	public function get_plugin_by( $key = '', $value = null ) {
		foreach ( $this->plugins as $plugin ) {
			if ( $plugin[ $key ] === $value ) {
				return $plugin;
			}
		}
		return false;
	}

	/**
	 * Makes a request to the ACF connect server.
	 *
	 * @since   5.5.10
	 *
	 * @param   string $endpoint The API endpoint.
	 * @param   array  $body     The body to post.
	 * @return  (array|string|WP_Error)
	 */
	public function request( $endpoint = '', $body = null ) {

		$site_url = acf_get_home_url();
		if ( empty( $site_url ) || ! is_string( $site_url ) ) {
			$site_url = '';
		}

		$headers = array(
			'X-ACF-Version' => ACF_VERSION,
			'X-ACF-URL'     => $site_url,
		);

		$url = "https://connect.advancedcustomfields.com/$endpoint";

		// Staging environment.
		if ( defined( 'ACF_DEV_API' ) && ACF_DEV_API ) {
			$url = trailingslashit( ACF_DEV_API ) . $endpoint;
			acf_log( $url, $body );
		}

		// Determine URL.
		if ( acf_is_pro() ) {
			$license_key = acf_pro_get_license_key();
			if ( empty( $license_key ) || ! is_string( $license_key ) ) {
				$license_key = '';
			}
			$headers['X-ACF-License'] = $license_key;
			$headers['X-ACF-Plugin']  = 'pro';
		} else {
			$headers['X-ACF-Plugin'] = 'acf';
		}

		// Make request.
		$raw_response = wp_remote_post(
			$url,
			array(
				'timeout' => 20,
				'body'    => $body,
				'headers' => $headers,
			)
		);

		// Handle response error.
		if ( is_wp_error( $raw_response ) ) {
			return $raw_response;

			// Handle http error.
		} elseif ( wp_remote_retrieve_response_code( $raw_response ) !== 200 ) {
			return new WP_Error( 'server_error', wp_remote_retrieve_response_message( $raw_response ) );
		}

		// Decode JSON response.
		$json = json_decode( wp_remote_retrieve_body( $raw_response ), true );

		// Allow non json value.
		if ( $json === null ) {
			return wp_remote_retrieve_body( $raw_response );
		}

		return $json;
	}

	/**
	 * Returns update information for the given plugin id.
	 *
	 * @since   5.5.10
	 *
	 * @param   string  $id          The plugin id such as 'pro'.
	 * @param   boolean $force_check Bypasses cached result. Defaults to false.
	 * @return  array|WP_Error
	 */
	public function get_plugin_info( $id = '', $force_check = false ) {
		$transient_name = 'acf_plugin_info_' . $id;

		// check cache but allow for $force_check override.
		if ( ! $force_check ) {
			$transient = get_transient( $transient_name );
			if ( $transient !== false ) {
				return $transient;
			}
		}

		$response = $this->request( 'v2/plugins/get-info?p=' . $id );

		// convert string (misc error) to WP_Error object.
		if ( is_string( $response ) ) {
			$response = new WP_Error( 'server_error', esc_html( $response ) );
		}

		// allow json to include expiration but force minimum and max for safety.
		$expiration = $this->get_expiration( $response, DAY_IN_SECONDS );

		// update transient.
		set_transient( $transient_name, $response, $expiration );

		return $response;
	}

	/**
	 * Returns specific data from the 'update-check' response.
	 *
	 * @since   5.7.2
	 *
	 * @param string  $basename    The plugin basename.
	 * @param boolean $force_check Bypasses cached result. Defaults to false.
	 * @return array|false
	 */
	public function get_plugin_update( $basename = '', $force_check = false ) {
		// get updates.
		$updates = $this->get_plugin_updates( $force_check );

		// check for and return update.
		if ( is_array( $updates ) && isset( $updates['plugins'][ $basename ] ) ) {
			return $updates['plugins'][ $basename ];
		}

		return false;
	}

	/**
	 * Checks if an update is available, but can't be updated to.
	 *
	 * @since   6.2.1
	 *
	 * @param string  $basename    The plugin basename.
	 * @param boolean $force_check Bypasses cached result. Defaults to false.
	 * @return array|false
	 */
	public function get_no_update( $basename = '', $force_check = false ) {
		// get updates.
		$updates = $this->get_plugin_updates( $force_check );

		// check for and return update.
		if ( is_array( $updates ) && isset( $updates['no_update'][ $basename ] ) ) {
			return $updates['no_update'][ $basename ];
		}

		return false;
	}


	/**
	 * Checks for plugin updates.
	 *
	 * @since   5.6.9
	 * @since   5.7.2 Added 'checked' comparison
	 *
	 * @param   boolean $force_check Bypasses cached result. Defaults to false.
	 * @return  array|WP_Error.
	 */
	public function get_plugin_updates( $force_check = false ) {
		$transient_name = 'acf_plugin_updates';

		// Don't call our site if no plugins have registered updates.
		if ( empty( $this->plugins ) ) {
			return array();
		}

		// Construct array of 'checked' plugins.
		// Sort by key to avoid detecting change due to "include order".
		$checked = array();
		foreach ( $this->plugins as $basename => $plugin ) {
			$checked[ $basename ] = $plugin['version'];
		}
		ksort( $checked );

		// $force_check prevents transient lookup.
		if ( ! $force_check ) {
			$transient = get_transient( $transient_name );

			// If cached response was found, compare $transient['checked'] against $checked and ignore if they don't match (plugins/versions have changed).
			if ( is_array( $transient ) ) {
				$transient_checked = isset( $transient['checked'] ) ? $transient['checked'] : array();
				if ( wp_json_encode( $checked ) !== wp_json_encode( $transient_checked ) ) {
					$transient = false;
				}
			}
			if ( $transient !== false ) {
				return $transient;
			}
		}

		$post = array(
			'plugins' => wp_json_encode( $this->plugins ),
			'wp'      => wp_json_encode(
				array(
					'wp_name'      => get_bloginfo( 'name' ),
					'wp_url'       => acf_get_home_url(),
					'wp_version'   => get_bloginfo( 'version' ),
					'wp_language'  => get_bloginfo( 'language' ),
					'wp_timezone'  => get_option( 'timezone_string' ),
					'wp_multisite' => (int) is_multisite(),
					'php_version'  => PHP_VERSION,
				)
			),
			'acf'     => wp_json_encode(
				array(
					'acf_version' => get_option( 'acf_version' ),
					'acf_pro'     => acf_is_pro(),
					'block_count' => function_exists( 'acf_pro_get_registered_block_count' ) ? acf_pro_get_registered_block_count() : 0,
				)
			),
		);

		// Check update from connect.
		$response = $this->request( 'v2/plugins/update-check', $post );

		// Append checked reference.
		if ( is_array( $response ) ) {
			$response['checked'] = $checked;

			if ( isset( $response['license_status'] ) && function_exists( 'acf_pro_update_license_status' ) ) {
				acf_pro_update_license_status( $response['license_status'] );
				unset( $response['license_status'] );
			}
		}

		// Allow json to include expiration but force minimum and max for safety.
		$expiration = $this->get_expiration( $response );

		// Update transient and return.
		set_transient( $transient_name, $response, $expiration );
		return $response;
	}

	/**
	 * This function safely gets the expiration value from a response.
	 *
	 * @since   5.6.9
	 *
	 * @param   mixed   $response The response from the server. Default false.
	 * @param   integer $min      The minimum expiration limit. Default 3 hours.
	 * @param   integer $max      The maximum expiration limit. Default 7 days.
	 * @return  integer
	 */
	public function get_expiration( $response = false, $min = 10800, $max = 604800 ) {
		$expiration = 0;

		// Check possible error conditions.
		if ( is_wp_error( $response ) || is_string( $response ) ) {
			return 15 * MINUTE_IN_SECONDS;
		}

		// Use the server requested expiration if present.
		if ( is_array( $response ) && isset( $response['expiration'] ) ) {
			$expiration = (int) $response['expiration'];
		}

		// Use the minimum if neither check matches, or ensure the server expiration isn't lower than our minimum.
		if ( $expiration < $min ) {
			return $min;
		}

		// Ensure the server expiration isn't higher than our max.
		if ( $expiration > $max ) {
			return $max;
		}

		return $expiration;
	}

	/**
	 * Deletes transients and allows a fresh lookup.
	 *
	 * @since   5.5.10
	 */
	public function refresh_plugins_transient() {
		delete_site_transient( 'update_plugins' );
		delete_transient( 'acf_plugin_updates' );
	}

	/**
	 * Called when WP updates the 'update_plugins' site transient. Used to inject ACF plugin update info.
	 *
	 * @since   5.0.0
	 *
	 * @param object $transient The current transient value.
	 * @return object $transient The modified transient value.
	 */
	public function modify_plugins_transient( $transient ) {

		// Bail early if no response (error).
		if ( ! isset( $transient->response ) ) {
			return $transient;
		}

		// Ensure no_update is set for back compat.
		if ( ! isset( $transient->no_update ) ) {
			$transient->no_update = array();
		}

		// Force-check (only once).
		$force_check = ( $this->checked == 0 ) ? ! empty( $_GET['force-check'] ) : false; // phpcs:ignore -- False positive, value not used.

		// Fetch updates (this filter is called multiple times during a single page load).
		$updates = $this->get_plugin_updates( $force_check );

		// Append ACF pro plugins.
		if ( is_array( $updates ) ) {
			if ( ! empty( $updates['plugins'] ) ) {
				foreach ( $updates['plugins'] as $basename => $update ) {
					$transient->response[ $basename ] = (object) $update;
				}
			}
			if ( ! empty( $updates['no_update'] ) ) {
				foreach ( $updates['no_update'] as $basename => $update ) {
					$transient->no_update[ $basename ] = (object) $update;
				}
			}
		}

		++$this->checked;

		return $transient;
	}

	/**
	 * Returns the plugin data visible in the 'View details' popup
	 *
	 * @since   5.0.0
	 *
	 * @param   object $result The current result of plugin data.
	 * @param   string $action The action being performed.
	 * @param   object $args   Data about the plugin being retried.
	 * @return  $result
	 */
	public function modify_plugin_details( $result, $action = null, $args = null ) {

		$plugin = false;

		// Only for 'plugin_information' action.
		if ( $action !== 'plugin_information' ) {
			return $result;
		}

		// Find plugin via slug.
		$plugin = $this->get_plugin_by( 'slug', $args->slug );
		if ( ! $plugin ) {
			return $result;
		}

		// Get data from connect or cache.
		$response = $this->get_plugin_info( $plugin['id'] );

		// Bail early if no response.
		if ( ! is_array( $response ) ) {
			return $result;
		}

		// Remove tags (different context).
		unset( $response['tags'] );

		// Convert to object.
		$response = (object) $response;

		$sections = array(
			'description'    => '',
			'installation'   => '',
			'changelog'      => '',
			'upgrade_notice' => '',
		);
		foreach ( $sections as $k => $v ) {
			$sections[ $k ] = $response->$k;
		}
		$response->sections = $sections;

		return $response;
	}
}