Created
November 17, 2017 13:45
-
-
Save robincornett/3546b6a78415a980875e88218f0c8436 to your computer and use it in GitHub Desktop.
Comparing updater classes in Envira 1.6.1.6 and 1.7.x (rewrite)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Updater class. | |
* | |
* @since 2.0.0 | |
* | |
* @package Envira_Gallery | |
* @author Thomas Griffin | |
*/ | |
class Envira_Gallery_Updater { | |
/** | |
* Plugin name. | |
* | |
* @since 2.0.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_name = false; | |
/** | |
* Plugin slug. | |
* | |
* @since 2.0.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_slug = false; | |
/** | |
* Plugin path. | |
* | |
* @since 2.0.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_path = false; | |
/** | |
* URL of the plugin. | |
* | |
* @since 2.0.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_url = false; | |
/** | |
* Remote URL for getting plugin updates. | |
* | |
* @since 2.0.0 | |
* | |
* @var bool|string | |
*/ | |
public $remote_url = false; | |
/** | |
* Version number of the plugin. | |
* | |
* @since 2.0.0 | |
* | |
* @var bool|int | |
*/ | |
public $version = false; | |
/** | |
* License key for the plugin. | |
* | |
* @since 2.0.0 | |
* | |
* @var bool|string | |
*/ | |
public $key = false; | |
/** | |
* Holds the update data returned from the API. | |
* | |
* @since 2.1.3 | |
* | |
* @var bool|object | |
*/ | |
public $update = false; | |
/** | |
* Holds the plugin info details for the update. | |
* | |
* @since 2.1.3 | |
* | |
* @var bool|object | |
*/ | |
public $info = false; | |
/** | |
* Path to the file. | |
* | |
* @since 2.0.0 | |
* | |
* @var string | |
*/ | |
public $file = __FILE__; | |
/** | |
* Holds the base class object. | |
* | |
* @since 2.0.0 | |
* | |
* @var object | |
*/ | |
public $base; | |
/** | |
* Primary class constructor. | |
* | |
* @since 2.0.0 | |
* | |
* @param array $config Array of updater config args. | |
*/ | |
public function __construct( array $config ) { | |
// Load the base class object. | |
$this->base = Envira_Gallery::get_instance(); | |
// Set class properties. | |
$accepted_args = array( | |
'plugin_name', | |
'plugin_slug', | |
'plugin_path', | |
'plugin_url', | |
'remote_url', | |
'version', | |
'key', | |
); | |
foreach ( $accepted_args as $arg ) { | |
$this->$arg = $config[ $arg ]; | |
} | |
// If the user cannot update plugins, stop processing here. | |
if ( ! current_user_can( 'update_plugins' ) ) { | |
return; | |
} | |
// Load the updater hooks and filters. | |
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'update_plugins_filter' ) ); | |
//add_filter( 'set_site_transient_update_plugins', array( $this, 'set_site_transient_update_plugins' ) ); | |
//add_filter( 'transient_update_plugins', array( $this, 'transient_update_plugins' ) ); | |
add_filter( 'http_request_args', array( $this, 'http_request_args' ), 10, 2 ); | |
add_filter( 'plugins_api', array( $this, 'plugins_api' ), 10, 3 ); | |
} | |
/** | |
* Infuse plugin update details when WordPress runs its update checker. | |
* | |
* @since 2.0.0 | |
* | |
* @param object $value The WordPress update object. | |
* | |
* @return object $value Amended WordPress update object on success, default if object is empty. | |
*/ | |
public function update_plugins_filter( $value ) { | |
// If no update object exists, return early. | |
if ( empty( $value ) ) { | |
return $value; | |
} | |
// Run update check by pinging the external API. If it fails, return the default update object. | |
if ( ! $this->update ) { | |
$this->update = $this->perform_remote_request( 'get-plugin-update', array( 'tgm-updater-plugin' => $this->plugin_slug ) ); | |
if ( ! $this->update || ! empty( $this->update->error ) ) { | |
$this->update = false; | |
return $value; | |
} | |
} | |
// Infuse the update object with our data if the version from the remote API is newer. | |
if ( isset( $this->update->new_version ) && version_compare( $this->version, $this->update->new_version, '<' ) ) { | |
// The $plugin_update object contains new_version, package, slug and last_update keys. | |
$value->response[ $this->plugin_path ] = $this->update; | |
} | |
// Return the update object. | |
return $value; | |
} | |
/** | |
* Disables SSL verification to prevent download package failures. | |
* | |
* @since 2.0.0 | |
* | |
* @param array $args Array of request args. | |
* @param string $url The URL to be pinged. | |
* | |
* @return array $args Amended array of request args. | |
*/ | |
public function http_request_args( $args, $url ) { | |
// If this is an SSL request and we are performing an upgrade routine, disable SSL verification. | |
if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'tgm-updater-action=get-plugin-update' ) ) { | |
$args['sslverify'] = false; | |
} | |
return $args; | |
} | |
/** | |
* Filters the plugins_api function to get our own custom plugin information | |
* from our private repo. | |
* | |
* @since 2.0.0 | |
* | |
* @param object $api The original plugins_api object. | |
* @param string $action The action sent by plugins_api. | |
* @param array $args Additional args to send to plugins_api. | |
* | |
* @return object $api New stdClass with plugin information on success, default response on failure. | |
*/ | |
public function plugins_api( $api, $action = '', $args = null ) { | |
$plugin = ( 'plugin_information' == $action ) && isset( $args->slug ) && ( $this->plugin_slug == $args->slug ); | |
// If our plugin matches the request, set our own plugin data, else return the default response. | |
if ( $plugin ) { | |
return $this->set_plugins_api( $api ); | |
} else { | |
return $api; | |
} | |
} | |
/** | |
* Pings a remote API to retrieve plugin information for WordPress to display. | |
* | |
* @since 2.0.0 | |
* | |
* @param object $default_api The default API object. | |
* | |
* @return object $api Return custom plugin information to plugins_api. | |
*/ | |
public function set_plugins_api( $default_api ) { | |
// Perform the remote request to retrieve our plugin information. If it fails, return the default object. | |
if ( ! $this->info ) { | |
$this->info = $this->perform_remote_request( 'get-plugin-info', array( 'tgm-updater-plugin' => $this->plugin_slug ) ); | |
if ( ! $this->info || ! empty( $this->info->error ) ) { | |
$this->info = false; | |
return $default_api; | |
} | |
} | |
// Create a new stdClass object and populate it with our plugin information. | |
$api = new stdClass; | |
$api->name = isset( $this->info->name ) ? $this->info->name : ''; | |
$api->slug = isset( $this->info->slug ) ? $this->info->slug : ''; | |
$api->version = isset( $this->info->version ) ? $this->info->version : ''; | |
$api->author = isset( $this->info->author ) ? $this->info->author : ''; | |
$api->author_profile = isset( $this->info->author_profile ) ? $this->info->author_profile : ''; | |
$api->requires = isset( $this->info->requires ) ? $this->info->requires : ''; | |
$api->tested = isset( $this->info->tested ) ? $this->info->tested : ''; | |
$api->last_updated = isset( $this->info->last_updated ) ? $this->info->last_updated : ''; | |
$api->homepage = isset( $this->info->homepage ) ? $this->info->homepage : ''; | |
$api->sections['changelog'] = isset( $this->info->changelog ) ? $this->info->changelog : ''; | |
$api->download_link = isset( $this->info->download_link ) ? $this->info->download_link : ''; | |
// Return the new API object with our custom data. | |
return $api; | |
} | |
/** | |
* Queries the remote URL via wp_remote_post and returns a json decoded response. | |
* | |
* @since 2.0.0 | |
* | |
* @param string $action The name of the $_POST action var. | |
* @param array $body The content to retrieve from the remote URL. | |
* @param array $headers The headers to send to the remote URL. | |
* @param string $return_format The format for returning content from the remote URL. | |
* | |
* @return string|bool Json decoded response on success, false on failure. | |
*/ | |
public function perform_remote_request( $action, $body = array(), $headers = array(), $return_format = 'json' ) { | |
// Build the body of the request. | |
$body = wp_parse_args( | |
$body, | |
array( | |
'tgm-updater-action' => $action, | |
'tgm-updater-key' => $this->key, | |
'tgm-updater-wp-version' => get_bloginfo( 'version' ), | |
'tgm-updater-referer' => site_url(), | |
) | |
); | |
$body = http_build_query( $body, '', '&' ); | |
// Build the headers of the request. | |
$headers = wp_parse_args( | |
$headers, | |
array( | |
'Content-Type' => 'application/x-www-form-urlencoded', | |
'Content-Length' => strlen( $body ), | |
) | |
); | |
// Setup variable for wp_remote_post. | |
$post = array( | |
'headers' => $headers, | |
'body' => $body, | |
); | |
// Perform the query and retrieve the response. | |
$response = wp_remote_post( esc_url_raw( $this->remote_url ), $post ); | |
$response_code = wp_remote_retrieve_response_code( $response ); | |
$response_body = wp_remote_retrieve_body( $response ); | |
// Bail out early if there are any errors. | |
if ( 200 != $response_code || is_wp_error( $response_body ) ) { | |
return false; | |
} | |
// Return the json decoded content. | |
return json_decode( $response_body ); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Updater class. | |
* | |
* @since 1.7.0 | |
* | |
* @package Envira_Gallery | |
* @author Envira Gallery Team | |
*/ | |
namespace Envira\Admin; | |
// Exit if accessed directly. | |
if ( ! defined( 'ABSPATH' ) ) { | |
exit; | |
} | |
use Envira\Admin\License; | |
class Updater { | |
/** | |
* Plugin name. | |
* | |
* @since 1.7.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_name = false; | |
/** | |
* Plugin slug. | |
* | |
* @since 1.7.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_slug = false; | |
/** | |
* Plugin path. | |
* | |
* @since 1.7.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_path = false; | |
/** | |
* URL of the plugin. | |
* | |
* @since 1.7.0 | |
* | |
* @var bool|string | |
*/ | |
public $plugin_url = false; | |
/** | |
* Remote URL for getting plugin updates. | |
* | |
* @since 1.7.0 | |
* | |
* @var bool|string | |
*/ | |
public $remote_url = false; | |
/** | |
* Version number of the plugin. | |
* | |
* @since 1.7.0 | |
* | |
* @var bool|int | |
*/ | |
public $version = false; | |
/** | |
* License key for the plugin. | |
* | |
* @since 1.7.0 | |
* | |
* @var bool|string | |
*/ | |
public $key = false; | |
/** | |
* Holds the update data returned from the API. | |
* | |
* @since 2.1.3 | |
* | |
* @var bool|object | |
*/ | |
public $update = false; | |
/** | |
* Holds the plugin info details for the update. | |
* | |
* @since 2.1.3 | |
* | |
* @var bool|object | |
*/ | |
public $info = false; | |
/** | |
* Primary class constructor. | |
* | |
* @since 1.7.0 | |
*/ | |
// public function __construct() { | |
public function __construct( array $config ) { | |
// Load the base class object. | |
// $this->base = Envira_Gallery::get_instance(); | |
// Set class properties. | |
// $accepted_args = array( | |
// 'plugin_name', | |
// 'plugin_slug', | |
// 'plugin_path', | |
// 'plugin_url', | |
// 'remote_url', | |
// 'version', | |
// 'key' | |
// ); | |
// foreach ( $accepted_args as $arg ) { | |
// $this->$arg = $config[$arg]; | |
// } | |
// If the user cannot update plugins, stop processing here. | |
if ( ! current_user_can( 'update_plugins' ) ) { | |
return; | |
} | |
// Load the updater hooks and filters. | |
add_filter( 'pre_set_site_transient_update_plugins', array( $this, 'update_plugins_filter' ) ); | |
//add_filter( 'set_site_transient_update_plugins', array( $this, 'set_site_transient_update_plugins' ) ); | |
//add_filter( 'transient_update_plugins', array( $this, 'transient_update_plugins' ) ); | |
add_filter( 'http_request_args', array( $this, 'http_request_args' ), 10, 2 ); | |
add_filter( 'plugins_api', array( $this, 'plugins_api' ), 10, 3 ); | |
} | |
/** | |
* Infuse plugin update details when WordPress runs its update checker. | |
* | |
* @since 1.7.0 | |
* | |
* @param object $value The WordPress update object. | |
* @return object $value Amended WordPress update object on success, default if object is empty. | |
*/ | |
public function update_plugins_filter( $value ) { | |
// If no update object exists, return early. | |
if ( empty( $value ) ) { | |
return $value; | |
} | |
// Run update check by pinging the external API. If it fails, return the default update object. | |
if ( ! $this->update ) { | |
$this->update = $this->perform_remote_request( 'get-plugin-update', array( 'tgm-updater-plugin' => $this->plugin_slug ) ); | |
if ( ! $this->update || ! empty( $this->update->error ) ) { | |
$this->update = false; | |
return $value; | |
} | |
} | |
// Infuse the update object with our data if the version from the remote API is newer. | |
if ( isset( $this->update->new_version ) && version_compare( $this->version, $this->update->new_version, '<' ) ) { | |
// The $plugin_update object contains new_version, package, slug and last_update keys. | |
$value->response[$this->plugin_path] = $this->update; | |
} | |
// Return the update object. | |
return $value; | |
} | |
/** | |
* Disables SSL verification to prevent download package failures. | |
* | |
* @since 1.7.0 | |
* | |
* @param array $args Array of request args. | |
* @param string $url The URL to be pinged. | |
* @return array $args Amended array of request args. | |
*/ | |
public function http_request_args( $args, $url ) { | |
// If this is an SSL request and we are performing an upgrade routine, disable SSL verification. | |
if ( strpos( $url, 'https://' ) !== false && strpos( $url, 'tgm-updater-action=get-plugin-update' ) ) { | |
$args['sslverify'] = false; | |
} | |
return $args; | |
} | |
/** | |
* Filters the plugins_api function to get our own custom plugin information | |
* from our private repo. | |
* | |
* @since 1.7.0 | |
* | |
* @param object $api The original plugins_api object. | |
* @param string $action The action sent by plugins_api. | |
* @param array $args Additional args to send to plugins_api. | |
* @return object $api New stdClass with plugin information on success, default response on failure. | |
*/ | |
public function plugins_api( $api, $action = '', $args = null ) { | |
$plugin = ( 'plugin_information' == $action ) && isset( $args->slug ) && ( $this->plugin_slug == $args->slug ); | |
// If our plugin matches the request, set our own plugin data, else return the default response. | |
if ( $plugin ) { | |
return $this->set_plugins_api( $api ); | |
} else { | |
return $api; | |
} | |
} | |
/** | |
* Pings a remote API to retrieve plugin information for WordPress to display. | |
* | |
* @since 1.7.0 | |
* | |
* @param object $default_api The default API object. | |
* @return object $api Return custom plugin information to plugins_api. | |
*/ | |
public function set_plugins_api( $default_api ) { | |
// Perform the remote request to retrieve our plugin information. If it fails, return the default object. | |
if ( ! $this->info ) { | |
$this->info = $this->perform_remote_request( 'get-plugin-info', array( 'tgm-updater-plugin' => $this->plugin_slug ) ); | |
if ( ! $this->info || ! empty( $this->info->error ) ) { | |
$this->info = false; | |
return $default_api; | |
} | |
} | |
// Create a new stdClass object and populate it with our plugin information. | |
$api = new stdClass; | |
$api->name = isset( $this->info->name ) ? $this->info->name : ''; | |
$api->slug = isset( $this->info->slug ) ? $this->info->slug : ''; | |
$api->version = isset( $this->info->version ) ? $this->info->version : ''; | |
$api->author = isset( $this->info->author ) ? $this->info->author : ''; | |
$api->author_profile = isset( $this->info->author_profile ) ? $this->info->author_profile : ''; | |
$api->requires = isset( $this->info->requires ) ? $this->info->requires : ''; | |
$api->tested = isset( $this->info->tested ) ? $this->info->tested : ''; | |
$api->last_updated = isset( $this->info->last_updated ) ? $this->info->last_updated : ''; | |
$api->homepage = isset( $this->info->homepage ) ? $this->info->homepage : ''; | |
$api->sections['changelog'] = isset( $this->info->changelog ) ? $this->info->changelog : ''; | |
$api->download_link = isset( $this->info->download_link ) ? $this->info->download_link : ''; | |
// Return the new API object with our custom data. | |
return $api; | |
} | |
/** | |
* Queries the remote URL via wp_remote_post and returns a json decoded response. | |
* | |
* @since 1.7.0 | |
* | |
* @param string $action The name of the $_POST action var. | |
* @param array $body The content to retrieve from the remote URL. | |
* @param array $headers The headers to send to the remote URL. | |
* @param string $return_format The format for returning content from the remote URL. | |
* @return string|bool Json decoded response on success, false on failure. | |
*/ | |
public function perform_remote_request( $action, $body = array(), $headers = array(), $return_format = 'json' ) { | |
// Build the body of the request. | |
$body = wp_parse_args( | |
$body, | |
array( | |
'tgm-updater-action' => $action, | |
'tgm-updater-key' => $this->key, | |
'tgm-updater-wp-version' => get_bloginfo( 'version' ), | |
'tgm-updater-referer' => site_url() | |
) | |
); | |
$body = http_build_query( $body, '', '&' ); | |
// Build the headers of the request. | |
$headers = wp_parse_args( | |
$headers, | |
array( | |
'Content-Type' => 'application/x-www-form-urlencoded', | |
'Content-Length' => strlen( $body ) | |
) | |
); | |
// Setup variable for wp_remote_post. | |
$post = array( | |
'headers' => $headers, | |
'body' => $body | |
); | |
// Perform the query and retrieve the response. | |
$response = wp_remote_post( esc_url_raw( $this->remote_url ), $post ); | |
$response_code = wp_remote_retrieve_response_code( $response ); | |
$response_body = wp_remote_retrieve_body( $response ); | |
// Bail out early if there are any errors. | |
if ( 200 != $response_code || is_wp_error( $response_body ) ) { | |
return false; | |
} | |
// Return the json decoded content. | |
return json_decode( $response_body ); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Compare the
__construct()
methods. In the new Updater class, the class properties are never actually defined. 1.7.x shows quite a few HTTP request timeouts in Query Monitor, even in a single site install.In a multisite install when another plugin is active which uses the EDD software licensing class, that plugin appears to inadvertently "hijack" Envira's updater class and use its
perform_remote_request
method. Since Envira does not check or set its own class properties in 1.7.x, itspre_set_site_transient_update_plugins
method (update_plugins_filter
, line 153) always runs, unless there is no update object. It looks like in 1.7.x, Envira is switching to using namespacing in environments which support it--the old updater is now in a Legacy folder and still seems to set the class properties.In multisite, EDD software licensing removes its own method on that filter, and then adds it back when its work is done. However, part of that work is using the
set_site_transient
function, which includes the apply_filters call that both updater classes use. The Envira method is run anytime the site_site_transient function is used (I expect more plugins than just EDDSL use this), but without any checks for its class properties, the HTTP request is made and times out. This bogs down the admin screen(s) in question.This also affects Soliloquy, and WPForms Pro, I expect, due to codebase similarities. I'm not sure why the new updater class has removed the bit in the
__construct
which sets all the class properties, but I think that it needs to be added back in, or if it's not necessary, theupdate_plugins_filter
needs to be updated to return early if$this->plugin_slug
is false.