: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
if ( $this->is_network_active() && fs_is_network_admin() ) {
if ( isset( $plugin_info['menu_network'] ) &&
is_array( $plugin_info['menu_network'] ) &&
! empty( $plugin_info['menu_network'] )
$plugin_info['menu'] = $plugin_info['menu_network'];
if ( ! isset( $plugin_info['menu'] ) ) {
$plugin_info['menu'] = array();
if ( ! empty( $this->_storage->sdk_last_version ) &&
version_compare( $this->_storage->sdk_last_version, '1.1.2', '<=' )
// Backward compatibility to 1.1.2
$plugin_info['menu']['slug'] = isset( $plugin_info['menu_slug'] ) ?
$plugin_info['menu_slug'] :
$this->_menu = FS_Admin_Menu_Manager::instance(
$this->get_unique_affix()
$this->_menu->init( $plugin_info['menu'], $this->is_addon() );
$this->_has_addons = $this->get_bool_option( $plugin_info, 'has_addons', false );
$this->_has_paid_plans = $this->get_bool_option( $plugin_info, 'has_paid_plans', true );
$this->_has_premium_version = $this->get_bool_option( $plugin_info, 'has_premium_version', $this->_has_paid_plans );
$this->_ignore_pending_mode = $this->get_bool_option( $plugin_info, 'ignore_pending_mode', false );
$this->_is_org_compliant = $this->get_bool_option( $plugin_info, 'is_org_compliant', true );
$this->_is_premium_only = $this->get_bool_option( $plugin_info, 'is_premium_only', false );
if ( $this->_is_premium_only ) {
// If premium only plugin, disable anonymous mode.
$this->_enable_anonymous = false;
$this->_anonymous_mode = false;
$this->_enable_anonymous = $this->get_bool_option( $plugin_info, 'enable_anonymous', true );
$this->_anonymous_mode = $this->get_bool_option( $plugin_info, 'anonymous_mode', false );
$this->_permissions = $this->get_option( $plugin_info, 'permissions', array() );
$this->_is_bundle_license_auto_activation_enabled = $this->get_option( $plugin_info, 'bundle_license_auto_activation', false );
if ( ! empty( $plugin_info['trial'] ) ) {
$this->_trial_days = $this->get_numeric_option(
// Default to 0 - trial without days specification.
$this->_is_trial_require_payment = $this->get_bool_option( $plugin_info['trial'], 'is_require_payment', false );
$this->_navigation = $this->get_option(
$this->is_free_wp_org_theme() ?
* @param string[] $options
private function get_option( &$options, $key, $default = false ) {
return ! empty( $options[ $key ] ) ? $options[ $key ] : $default;
private function get_bool_option( &$options, $key, $default = false ) {
return isset( $options[ $key ] ) && is_bool( $options[ $key ] ) ? $options[ $key ] : $default;
private function get_numeric_option( &$options, $key, $default = false ) {
return isset( $options[ $key ] ) && is_numeric( $options[ $key ] ) ? $options[ $key ] : $default;
* @author Vova Feldman (@svovaf)
private function should_stop_execution() {
if ( empty( $this->_storage->was_plugin_loaded ) ) {
* Don't execute Freemius until plugin was fully loaded at least once,
* to give the opportunity for the activation hook to run before pinging
* the API for connectivity test. This logic is relevant for the
* identification of new plugin install vs. plugin update.
* @author Vova Feldman (@svovaf)
if ( $this->is_activation_mode() ) {
* If in activation mode, don't execute Freemius outside the admin dashboard.
* @author Vova Feldman (@svovaf)
if ( ! WP_FS__IS_HTTP_REQUEST ) {
* If in activation and executed without HTTP context (e.g. CLI, Cronjob),
* then don't start Freemius.
* @author Vova Feldman (@svovaf)
* @link https://wordpress.org/support/topic/errors-in-the-freemius-class-when-running-in-wordpress-in-cli
* If in activation mode, don't execute Freemius during wp crons
* (wp crons have HTTP context - called as HTTP request).
* @author Vova Feldman (@svovaf)
* During activation, if running in AJAX mode, unless there's a sticky
* connectivity issue notice, don't run Freemius.
* @author Vova Feldman (@svovaf)
* Triggered after code type has changed.
* @author Vova Feldman (@svovaf)
function _after_code_type_change() {
$this->_logger->entrance();
if ( $this->is_theme() ) {
// Expire the cache of the previous tabs since the theme may
// have setting updates after code type has changed.
$this->_cache->expire( 'tabs' );
$this->_cache->expire( 'tabs_stylesheets' );
if ( ! $this->is_addon() ) {
is_admin() ? 'admin_init' : 'init',
array( &$this, '_plugin_code_type_changed' )
if ( $this->is_registered() && $this->is_premium() ) {
// Purge cached payments after switching to the premium version.
// @todo This logic doesn't handle purging the cache for serviceware module upgrade.
$this->get_api_user_scope()->purge_cache( "/plugins/{$this->_module_id}/payments.json?include_addons=true" );
* Handles plugin's code type change (free <--> premium).
* @author Vova Feldman (@svovaf)
function _plugin_code_type_changed() {
$this->_logger->entrance();
if ( $this->is_premium() ) {
$this->reconnect_locally();
// Activated premium code.
$this->do_action( 'after_premium_version_activation' );
// Remove all sticky messages related to download of the premium version.
$this->_admin_notices->remove_sticky( array(
if ( ! $this->is_only_premium() ) {
$notice = sprintf( $this->get_text_inline( 'Premium %s version was successfully activated.', 'premium-activated-message' ), $this->_module_type );
$license_notice = $this->get_license_network_activation_notice();
if ( ! empty( $license_notice ) ) {
$notice .= ' ' . $license_notice;
if ( ! empty( $notice ) ) {
$this->_admin_notices->add_sticky(
$this->get_text_x_inline( 'W00t',
'Used to express elation, enthusiasm, or triumph (especially in electronic communication).', 'woot' ) . '!'
// Remove sticky message related to premium code activation.
$this->_admin_notices->remove_sticky( 'premium_activated' );
// Activated free code (after had the premium before).
$this->do_action( 'after_free_version_reactivation' );
if ( $this->is_paying() && ! $this->is_premium() ) {
$this->add_complete_upgrade_instructions_notice(
/* translators: %s: License type (e.g. you have a professional license) */
$this->get_text_inline( 'You have a %s license.', 'you-have-x-license' ),
if ( $this->is_registered() ) {
// Schedule code type changes event.
$this->schedule_install_sync();
* Unregister the uninstall hook for the other version of the plugin (with different code type) to avoid
* triggering a fatal error when uninstalling that plugin. For example, after deactivating the "free" version
* of a specific plugin, its uninstall hook should be unregistered after the "premium" version has been
* activated. If we don't do that, a fatal error will occur when we try to uninstall the "free" version since
* the main file of the "free" version will be loaded first before calling the hooked callback. Since the
* free and premium versions are almost identical (same class or have same functions), a fatal error like
* "Cannot redeclare class MyClass" or "Cannot redeclare my_function()" will occur.
$this->unregister_uninstall_hook();
$this->clear_module_main_file_cache();
// Update is_premium of latest version.
$this->_storage->prev_is_premium = $this->_plugin->is_premium;
#----------------------------------------------------------------------------------
#----------------------------------------------------------------------------------
* Check if add-on installed and activated on site.
* @author Vova Feldman (@svovaf)
* @param string|number $id_or_slug
* @param bool|null $is_premium Since 1.2.1.7 can check for specified add-on version.
function is_addon_activated( $id_or_slug, $is_premium = null ) {
$this->_logger->entrance();
$addon_id = self::get_module_id( $id_or_slug );
$is_activated = self::has_instance( $addon_id );
if ( is_bool( $is_premium ) ) {
// Check if the specified code version is activate.
$addon = $this->get_addon_instance( $addon_id );
$is_activated = ( $is_premium === $addon->is_premium() );
* Check if add-on was connected to install
* @author Vova Feldman (@svovaf)
* @param string|number $id_or_slug
function is_addon_connected( $id_or_slug ) {
$this->_logger->entrance();
$sites = self::get_all_sites( WP_FS__MODULE_TYPE_PLUGIN );
$addon_id = self::get_module_id( $id_or_slug );
$addon = $this->get_addon( $addon_id );
if ( ! isset( $sites[ $slug ] ) ) {
$plugin = FS_Plugin_Manager::instance( $addon_id )->get();
if ( $plugin->parent_plugin_id != $this->_plugin->id ) {
// The given slug do NOT belong to any of the plugin's add-ons.
return ( is_object( $site ) &&
is_numeric( $site->id ) &&
is_numeric( $site->user_id ) &&
FS_Plugin_Plan::is_valid_id( $site->plan_id )
* Determines if add-on installed.
* NOTE: This is a heuristic and only works if the folder/file named as the slug.
* @author Vova Feldman (@svovaf)
* @param string|number $id_or_slug
function is_addon_installed( $id_or_slug ) {
$this->_logger->entrance();
$addon_id = self::get_module_id( $id_or_slug );
return file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $this->get_addon_basename( $addon_id ) ) );
* @author Vova Feldman (@svovaf)
* @param string|number $id_or_slug
function get_addon_basename( $id_or_slug ) {
$addon_id = self::get_module_id( $id_or_slug );
if ( $this->is_addon_activated( $addon_id ) ) {
return self::instance( $addon_id )->get_plugin_basename();
$addon = $this->get_addon( $addon_id );
$premium_basename = "{$addon->premium_slug}/{$addon->slug}.php";
if ( file_exists( fs_normalize_path( WP_PLUGIN_DIR . '/' . $premium_basename ) ) ) {
return $premium_basename;
$all_plugins = $this->get_all_plugins();
foreach ( $all_plugins as $basename => $data ) {
if ( $addon->slug === $data['slug'] ||
$addon->premium_slug === $data['slug']
$free_basename = "{$addon->slug}/{$addon->slug}.php";
* Get installed add-ons instances.
* @author Vova Feldman (@svovaf)
function get_installed_addons() {
if ( $this->is_addon() ) {
// Add-on cannot have add-ons.
$installed_addons = array();
foreach ( self::$_instances as $instance ) {
if ( $instance->is_addon_of( $this->_plugin->id ) ) {
$installed_addons[] = $instance;
return $installed_addons;
* Check if any add-ons of the plugin are installed.
* @author Leo Fajardo (@leorw)
function has_installed_addons() {
if ( ! $this->has_addons() ) {
foreach ( self::$_instances as $instance ) {
if ( $instance->is_addon() && is_object( $instance->_parent_plugin ) ) {
if ( $this->_plugin->id == $instance->_parent_plugin->id ) {
* Tell Freemius that the current plugin is an add-on.
* @author Vova Feldman (@svovaf)
* @param number $parent_plugin_id The parent plugin ID
function init_addon( $parent_plugin_id ) {
$this->_plugin->parent_plugin_id = $parent_plugin_id;
* @author Vova Feldman (@svovaf)
isset( $this->_plugin->parent_plugin_id ) &&
is_numeric( $this->_plugin->parent_plugin_id )
* @author Vova Feldman (@svovaf)
* @param number $parent_product_id
function is_addon_of( $parent_product_id ) {
$parent_product_id == $this->_plugin->parent_plugin_id
* Deactivate add-on if it's premium only and the user does't have a valid license.
* @param bool $is_after_trial_cancel
* @return bool If add-on was deactivated.