: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$this->_logger->entrance( 'slug = ' . $this->_slug );
if ( $check_user && ! current_user_can( 'activate_plugins' ) ) {
$uninstall_reason = null;
if ( isset( $this->_storage->uninstall_reason ) ) {
$uninstall_reason = $this->_storage->uninstall_reason;
$params['reason_id'] = $uninstall_reason->id;
$params['reason_info'] = $uninstall_reason->info;
if ( ! $this->is_registered() ) {
// Send anonymous uninstall event only if user submitted a feedback.
if ( isset( $uninstall_reason ) ) {
if ( isset( $uninstall_reason->is_anonymous ) && ! $uninstall_reason->is_anonymous ) {
$this->opt_in( false, false, false, false, true );
$params['uid'] = $this->get_anonymous_id();
$this->get_api_plugin_scope()->call( 'uninstall.json', 'put', $params );
$params = array_merge( $params, array(
'is_uninstalled' => true,
if ( $this->_is_network_active ) {
$this->send_installs_update( $params );
// Send uninstall event and handle the result.
$this->sync_install( $params );
// @todo Decide if we want to delete plugin information from db.
* Set the basename of the current product and hook _activate_plugin_event_hook() to the activation action.
* @author Vova Feldman (@svovaf)
* @param string $is_premium
function set_basename( $is_premium, $caller ) {
$basename = plugin_basename( $caller );
$current_basename = $is_premium ?
$this->_premium_plugin_basename :
$this->_free_plugin_basename;
if ( $current_basename == $basename ) {
// Basename value set correctly.
$this->_premium_plugin_basename = $basename;
$this->_free_plugin_basename = $basename;
$plugin_dir = dirname( $this->_plugin_dir_path ) . '/';
register_activation_hook(
array( &$this, '_activate_plugin_event_hook' )
* @author Vova Feldman (@svovaf)
* @since 2.2.1 If the context product is in its premium version, use the current module's basename, even if it was renamed.
function premium_plugin_basename() {
if ( ! isset( $this->_premium_plugin_basename ) ) {
$this->_premium_plugin_basename = $this->is_premium() ?
// The product is premium, so use the current basename.
$this->_plugin_basename :
$this->get_premium_slug() . '/' . basename( $this->_free_plugin_basename );
return $this->_premium_plugin_basename;
* Uninstall plugin hook. Called only when connected his account with Freemius for active sites tracking.
* @author Vova Feldman (@svovaf)
public static function _uninstall_plugin_hook() {
self::_load_required_static();
self::$_static_logger->entrance();
if ( ! current_user_can( 'activate_plugins' ) ) {
$plugin_file = substr( current_filter(), strlen( 'uninstall_' ) );
self::$_static_logger->info( 'plugin = ' . $plugin_file );
define( 'WP_FS__UNINSTALL_MODE', true );
$fs = self::get_instance_by_file( $plugin_file );
if ( is_object( $fs ) ) {
$fs->remove_sdk_reference();
self::require_plugin_essentials();
if ( is_plugin_active( $fs->_free_plugin_basename ) ||
is_plugin_active( $fs->premium_plugin_basename() )
// Deleting Free or Premium plugin version while the other version still installed.
* If there's a context install, run this method only when there's also a context user (e.g., when cloning a subsite of a multisite network into a single-site installation, it's possible for an install to be associated with a non-existing user entity; we want Freemius to be off in this case, while we are trying to recover the user).
( ! is_object( $fs->_site ) || $fs->is_registered() )
$fs->_uninstall_plugin_event();
$fs->do_action( 'after_uninstall' );
#----------------------------------------------------------------------------------
#region Plugin Information
#----------------------------------------------------------------------------------
* Load WordPress core plugin.php essential module.
* @author Vova Feldman (@svovaf)
private static function require_plugin_essentials() {
if ( ! function_exists( 'get_plugins' ) ) {
self::$_static_logger->log( 'Including wp-admin/includes/plugin.php...' );
require_once ABSPATH . 'wp-admin/includes/plugin.php';
* Load WordPress core pluggable.php module.
* @author Vova Feldman (@svovaf)
private static function require_pluggable_essentials() {
if ( ! function_exists( 'wp_get_current_user' ) ) {
require_once ABSPATH . 'wp-includes/pluggable.php';
* @author Vova Feldman (@svovaf)
* @param bool $reparse_plugin_metadata
function get_plugin_data( $reparse_plugin_metadata = false ) {
if ( ! isset( $this->_plugin_data ) || $reparse_plugin_metadata ) {
self::require_plugin_essentials();
if ( $this->is_plugin() ) {
* @author Vova Feldman (@svovaf)
* @since 1.2.0 When using get_plugin_data() do NOT translate plugin data.
* @link https://github.com/Freemius/wordpress-sdk/issues/77
$plugin_data = get_plugin_data(
$this->_plugin_main_file_path,
$theme_data = wp_get_theme();
if ( $this->_plugin_basename !== $theme_data->get_stylesheet() && is_child_theme() ) {
$parent_theme = $theme_data->parent();
if ( ( $parent_theme instanceof WP_Theme ) && $this->_plugin_basename === $parent_theme->get_stylesheet() ) {
$theme_data = $parent_theme;
'Name' => $theme_data->get( 'Name' ),
'Version' => $theme_data->get( 'Version' ),
'Author' => $theme_data->get( 'Author' ),
'Description' => $theme_data->get( 'Description' ),
'PluginURI' => $theme_data->get( 'ThemeURI' ),
$this->_plugin_data = $plugin_data;
return $this->_plugin_data;
* @author Vova Feldman (@svovaf)
* @since 1.2.2.5 If slug not set load slug by module ID.
* @return string Plugin slug.
if ( ! isset( $this->_slug ) ) {
$id_slug_type_path_map = self::$_accounts->get_option( 'id_slug_type_path_map', array() );
$this->_slug = $id_slug_type_path_map[ $this->_module_id ]['slug'];
* @author Leo Fajardo (@leorw)
function get_premium_slug() {
return ( is_object( $this->_plugin ) && ! empty( $this->_plugin->premium_slug ) ) ?
$this->_plugin->premium_slug :
"{$this->_slug}-premium";
* Retrieve the desired folder name for the product.
* @author Vova Feldman (@svovaf)
* @return string Plugin slug.
function get_target_folder_name() {
return $this->can_use_premium_code() ?
$this->_plugin->premium_slug :
* @author Vova Feldman (@svovaf)
* @return number Plugin ID.
return $this->_plugin->id;
* @author Leo Fajardo (@leorw)
* @return number|null Bundle ID.
function get_bundle_id() {
return ( isset( $this->_plugin->bundle_id ) && FS_Plugin::is_valid_id( $this->_plugin->bundle_id ) ) ?
$this->_plugin->bundle_id :
* @author Vova Feldman (@svovaf)
* @return string|null Bundle public key.
function get_bundle_public_key() {
return isset( $this->_plugin->bundle_public_key ) ?
$this->_plugin->bundle_public_key :
* Get whether the SDK has been initiated in the context of a Bundle.
* This will return true, if `bundle_id` is present in the SDK init parameters.
* $my_fs = fs_dynamic_init( array(
* 'bundle_id' => 'XXXX', // Will return true since we have bundle id.
* 'bundle_public_key' => 'pk_XXXX',
* @author Swashata Ghosh (@swashata)
* @return bool True if we are running in bundle context, false otherwise.
private function has_bundle_context() {
return ! is_null( $this->get_bundle_id() );
* @author Vova Feldman (@svovaf)
* @return string Freemius SDK version
function get_sdk_version() {
* @author Vova Feldman (@svovaf)
* @return number Parent plugin ID (if parent exist).
function get_parent_id() {
return $this->is_addon() ?
$this->get_parent_instance()->get_id() :
* @author Vova Feldman (@svovaf)
function get_usage_tracking_terms_url() {
return $this->apply_filters(
'usage_tracking_terms_url',
"https://freemius.com/product/opt-in/{$this->_plugin->id}/{$this->_slug}/"
* @todo (For LiteSDK) We can refactor this and other related functions giving links to several landing pages on freemius.com to come from a separate class like `FS_Terms_Pages`. This would get a `FS_WP_Hook` (hypothetical) instance as a dependency and use it to hook into the `license_activation_terms_url` or related filters. The entry level instance from `ms_fs()` would hold a public read-only variable `my_fs()->terms_pages` which would be an instance of `FS_Terms_Pages` and would hold all the links to the terms pages.
function get_license_activation_terms_url() {
return $this->apply_filters(
'license_activation_terms_url',
"https://freemius.com/product/license-activation/{$this->_plugin->id}/{$this->_slug}/"
* @author Vova Feldman (@svovaf)
function get_eula_url() {
return $this->apply_filters(
"https://freemius.com/product/{$this->_plugin->id}/{$this->_slug}/legal/eula/"
* @author Vova Feldman (@svovaf)
* @return string Plugin public key.
function get_public_key() {
return $this->_plugin->public_key;
* Will be available only on sandbox mode.
* @author Vova Feldman (@svovaf)
* @return mixed Plugin secret key.
function get_secret_key() {
return $this->_plugin->secret_key;
* @author Vova Feldman (@svovaf)
function has_secret_key() {
return ! empty( $this->_plugin->secret_key );
* @author Vova Feldman (@svovaf)
* @param string|bool $premium_suffix
function get_plugin_name( $premium_suffix = false ) {
$this->_logger->entrance();
* This `if-else` can be squeezed into a single `if` but I intentionally split it for code readability.
if ( ! isset( $this->_plugin_name ) ) {
$this->set_name( $premium_suffix );
! empty( $premium_suffix ) &&
( ! is_object( $this->_plugin ) || $this->_plugin->premium_suffix !== $premium_suffix )
// Name is already set, but there's a change in the premium suffix.
$this->set_name( $premium_suffix );
return $this->_plugin_name;
* Calculates and stores the product's name. This helper function was created specifically for get_plugin_name() just to make the code clearer.
* @author Vova Feldman (@svovaf)
* @param string $premium_suffix
private function set_name( $premium_suffix = '' ) {
$plugin_data = $this->get_plugin_data();
$this->_plugin_name = $plugin_data['Name'];
if ( is_string( $premium_suffix ) ) {
$premium_suffix = trim( $premium_suffix );
if ( ! empty( $premium_suffix ) ) {
// Check if plugin name contains " (premium)" or a custom suffix and remove it.
$suffix = ( ' ' . strtolower( $premium_suffix ) );
$suffix_len = strlen( $suffix );
if ( strlen( $plugin_data['Name'] ) > $suffix_len &&
$suffix === substr( strtolower( $plugin_data['Name'] ), - $suffix_len )
$this->_plugin_name = substr( $plugin_data['Name'], 0, - $suffix_len );
$this->_logger->departure( 'Name = ' . $this->_plugin_name );
* @author Vova Feldman (@svovaf)
* @param bool $reparse_plugin_metadata
function get_plugin_version( $reparse_plugin_metadata = false ) {
$this->_logger->entrance();
$plugin_data = $this->get_plugin_data( $reparse_plugin_metadata );
$this->_logger->departure( 'Version = ' . $plugin_data['Version'] );
return $this->apply_filters( 'plugin_version', $plugin_data['Version'] );