: str_replace(): Passing null to parameter #2 ($replace) of type array|string is deprecated in
$base_name_split = explode( '/', $this->_plugin_basename );
$this->_plugin_dir_name = $base_name_split[0];
if ( $this->_logger->is_on() ) {
$this->_logger->info( 'plugin_main_file_path = ' . $this->_plugin_main_file_path );
$this->_logger->info( 'plugin_dir_path = ' . $this->_plugin_dir_path );
$this->_logger->info( 'plugin_basename = ' . $this->_plugin_basename );
$this->_logger->info( 'free_plugin_basename = ' . $this->_free_plugin_basename );
$this->_logger->info( 'plugin_dir_name = ' . $this->_plugin_dir_name );
// Remember link between file to slug.
$this->store_file_slug_map();
// Store plugin's initial install timestamp.
if ( ! isset( $this->_storage->install_timestamp ) ) {
$this->_storage->install_timestamp = WP_FS__SCRIPT_START_TIME;
if ( ! is_object( $this->_plugin ) ) {
$this->_plugin = FS_Plugin_Manager::instance( $this->_module_id )->get();
$this->_admin_notices = FS_Admin_Notices::instance(
$this->_slug . ( $this->is_theme() ? ':theme' : '' ),
* Ensure that the admin notice will always have a title by using the stored plugin title if available and
* retrieving the title via the "get_plugin_name" method if there is no stored plugin title available.
* @author Leo Fajardo (@leorw)
( is_object( $this->_plugin ) && isset( $this->_plugin->title ) ?
$this->get_unique_affix()
if ( 'true' === fs_request_get( 'fs_clear_api_cache' ) ||
fs_request_is_action( 'restart_freemius' )
$this->register_constructor_hooks();
* Starting from version 2.0.0, `FS_Site` entities no longer have the `plan` property and have `plan_id`
* instead. This should be called before calling `_load_account()`, otherwise, `$this->_site` will not be
* loaded in `_load_account` for versions of SDK starting from 2.0.0.
* @author Leo Fajardo (@leorw)
self::migrate_install_plan_to_plan_id( $this->_storage );
$this->_version_updates_handler();
* @author Leo Fajardo (@leorw)
private function maybe_adjust_storage() {
$install_timestamp = null;
$options_to_update = array();
$is_network_admin = fs_is_network_admin();
$network_install_timestamp = $this->_storage->get( 'install_timestamp', null, true );
if ( ! $is_network_admin ) {
if ( is_null( $network_install_timestamp ) ) {
// Plugin was not network-activated before.
if ( is_null( $this->_storage->get( 'install_timestamp', null, false ) ) ) {
// Set the `install_timestamp` only if it's not yet set.
$install_timestamp = $network_install_timestamp;
$prev_is_premium = $this->_storage->get( 'prev_is_premium', null, true );
$current_wp_user = self::_get_current_wp_user();
$current_fs_user = self::_get_user_by_email( $current_wp_user->user_email );
$network_user_info = array();
$sites = self::get_sites();
$sites_count = count( $sites );
$blog_id_2_install_map = array();
$is_first_non_ignored_blog = true;
foreach ( $sites as $site ) {
$blog_id = self::get_site_blog_id( $site );
$blog_install_timestamp = $this->_storage->get( 'install_timestamp', null, $blog_id );
if ( is_null( $blog_install_timestamp ) ) {
// Plugin has not been installed on this blog.
! is_null( $install_timestamp ) &&
$blog_install_timestamp < $install_timestamp
$install = $this->get_install_by_blog_id( $blog_id );
$update_network_user_info = false;
if ( ! is_object( $install ) ) {
if ( ! $this->_storage->get( 'is_anonymous', false, $blog_id ) ) {
// The opt-in decision (whether to skip or opt in) is yet to be made.
$blog_id_2_install_map[ $blog_id ] = $install;
if ( empty( $network_user_info ) ) {
// Set the network user info for the 1st time. Choose any user information whether or not it is for the current WP user.
$update_network_user_info = true;
if ( ! $update_network_user_info &&
is_object( $current_fs_user ) &&
$network_user_info['user_id'] != $current_fs_user->id &&
$install->user_id == $current_fs_user->id
// If an install that is owned by the current WP user is found, use its user information instead.
$update_network_user_info = true;
if ( ! $update_network_user_info &&
( ! is_object( $current_fs_user ) || $current_fs_user->id == $install->user_id )
// Update to the earliest install info if there's no install found so far that is owned by the current WP user; OR only if the found install is owned by the current WP user.
$update_network_user_info = true;
if ( $update_network_user_info ) {
$network_user_info = array(
'user_id' => $install->user_id,
$site_prev_is_premium = $this->_storage->get( 'prev_is_premium', null, $blog_id );
if ( $is_first_non_ignored_blog ) {
$prev_is_premium = $site_prev_is_premium;
if ( is_null( $network_install_timestamp ) ) {
$install_timestamp = $blog_install_timestamp;
$is_first_non_ignored_blog = false;
if ( ! is_null( $prev_is_premium ) && $prev_is_premium !== $site_prev_is_premium ) {
// If a different `$site_prev_is_premium` value is found, do not include the option in the collection of options to update.
if ( $is_earlier_install ) {
// If an earlier install timestamp is found.
$install_timestamp = $blog_install_timestamp;
$installs_count = count( $blog_id_2_install_map );
if ( $sites_count === ( $installs_count + $skips_count ) ) {
if ( ! empty( $network_user_info ) ) {
$options_to_update['network_user_id'] = $network_user_info['user_id'];
$options_to_update['network_install_blog_id'] = $network_user_info['blog_id'];
foreach ( $blog_id_2_install_map as $blog_id => $install ) {
if ( $install->user_id == $network_user_info['user_id'] ) {
$this->_storage->store( 'is_delegated_connection', true, $blog_id );
if ( $sites_count === $skips_count ) {
* Assume network-level skipping as the intended action if all actions identified were only
* skipping of the connection (i.e., no opt-ins and delegated connections so far).
$options_to_update['is_anonymous_ms'] = true;
} else if ( $sites_count === $installs_count ) {
* Assume network-level opt-in as the intended action if all actions identified were only opt-ins
* (i.e., no delegation and skipping of the connections so far).
$options_to_update['is_network_connected'] = true;
if ( ! is_null( $install_timestamp ) ) {
$options_to_update['install_timestamp'] = $install_timestamp;
if ( ! is_null( $prev_is_premium ) ) {
$options_to_update['prev_is_premium'] = $prev_is_premium;
if ( ! empty( $options_to_update ) ) {
$this->adjust_storage( $options_to_update, $is_network_admin );
* @author Leo Fajardo (@leorw)
* @param bool $is_network_admin
private function adjust_storage( $options, $is_network_admin ) {
foreach ( $options as $name => $value ) {
$this->_storage->store( $name, $value, $is_network_admin ? true : null );
* Checks whether this module has a settings menu.
* @author Leo Fajardo (@leorw)
function has_settings_menu() {
return ( $this->_is_network_active && fs_is_network_admin() ) ?
$this->_menu->has_network_menu() :
$this->_menu->has_menu();
* If `true` the opt-in should be shown as a modal dialog box on the themes.php page. WordPress.org themes guidelines prohibit from redirecting the user from the themes.php page after activating a theme.
* @author Vova Feldman (@svovaf)
function show_opt_in_on_themes_page() {
if ( ! $this->is_free_wp_org_theme() ) {
if ( ! $this->has_settings_menu() ) {
return $this->show_settings_with_tabs();
* If `true` the opt-in should be shown on the product's main setting page.
* @author Vova Feldman (@svovaf)
* @uses show_opt_in_on_themes_page();
function show_opt_in_on_setting_page() {
return ! $this->show_opt_in_on_themes_page();
* If `true` the settings should be shown using tabs.
* @author Vova Feldman (@svovaf)
function show_settings_with_tabs() {
return ( self::NAVIGATION_TABS === $this->_navigation );
* Check if the context module is free wp.org theme.
* This method is helpful because:
* 1. wp.org themes are limited to a single submenu item,
* and sub-submenu items are most likely not allowed (never verified).
* 2. wp.org themes are not allowed to redirect the user
* after the theme activation, therefore, the agreed UX
* is showing the opt-in as a modal dialog box after
* activation (approved by @otto42, @emiluzelac, @greenshady, @grapplerulrich).
* @author Vova Feldman (@svovaf)
function is_free_wp_org_theme() {
$this->is_org_repo_compliant() &&
* Checks whether this a submenu item is visible.
* @author Vova Feldman (@svovaf)
* @since 1.2.2.7 Even if the menu item was specified to be hidden, when it is the context page, then show the submenu item so the user will have the right context page.
* @param bool $is_tabs_visibility_check This is used to decide if the associated tab should be shown or hidden.
function is_submenu_item_visible( $slug, $is_tabs_visibility_check = false ) {
if ( $this->is_admin_page( $slug ) ) {
* It is the current context page, so show the submenu item
* so the user will have the right context page, even if it
if ( ! $this->has_settings_menu() ) {
// No menu settings at all.
! $is_tabs_visibility_check &&
$this->is_org_repo_compliant() &&
$this->show_settings_with_tabs()
* wp.org themes are limited to a single submenu item, and
* sub-submenu items are most likely not allowed (never verified).
return $this->_menu->is_submenu_item_visible( $slug );
* Check if a Freemius page should be accessible via the UI.
* @author Vova Feldman (@svovaf)
function is_page_visible( $slug ) {
if ( $this->is_admin_page( $slug ) ) {
return $this->_menu->is_submenu_item_visible( $slug, true, true );
* @author Vova Feldman (@svovaf)
private function _version_updates_handler() {
if ( ! isset( $this->_storage->sdk_version ) || $this->_storage->sdk_version != $this->version ) {
// Freemius version upgrade mode.
$this->_storage->sdk_last_version = $this->_storage->sdk_version;
$this->_storage->sdk_version = $this->version;
if ( empty( $this->_storage->sdk_last_version ) ||
version_compare( $this->_storage->sdk_last_version, $this->version, '<' )
$this->_storage->sdk_upgrade_mode = true;
$this->_storage->sdk_downgrade_mode = false;
$this->_storage->sdk_downgrade_mode = true;
$this->_storage->sdk_upgrade_mode = false;
$this->do_action( 'sdk_version_update', $this->_storage->sdk_last_version, $this->version );
$plugin_version = $this->get_plugin_version();
if ( ! isset( $this->_storage->plugin_version ) || $this->_storage->plugin_version != $plugin_version ) {
// Plugin version upgrade mode.
$this->_storage->plugin_last_version = $this->_storage->plugin_version;
$this->_storage->plugin_version = $plugin_version;
if ( empty( $this->_storage->plugin_last_version ) ||
version_compare( $this->_storage->plugin_last_version, $plugin_version, '<' )
$this->_storage->plugin_upgrade_mode = true;
$this->_storage->plugin_downgrade_mode = false;
$this->_storage->plugin_downgrade_mode = true;
$this->_storage->plugin_upgrade_mode = false;
if ( ! empty( $this->_storage->plugin_last_version ) ) {
// Different version of the plugin was installed before, therefore it's an update.
$this->_storage->is_plugin_new_install = false;
$this->do_action( 'plugin_version_update', $this->_storage->plugin_last_version, $plugin_version );
#--------------------------------------------------------------------------------
#region Data Migration on SDK Update
#--------------------------------------------------------------------------------
* @author Vova Feldman (@svovaf)
* @param string $sdk_prev_version
* @param string $sdk_version
function _sdk_version_update( $sdk_prev_version, $sdk_version ) {
if ( empty( $sdk_prev_version ) ) {
version_compare( $sdk_prev_version, '2.5.1', '<' ) &&
version_compare( $sdk_version, '2.5.1', '>=' )
if ( $this->is_registered( true ) ) {
* Migrate to new permissions layer.
require_once WP_FS__DIR_INCLUDES . '/supplements/fs-migration-2.5.1.php';
$install_by_blog_id = is_multisite() ?
$this->get_blog_install_map() :
array( 0 => $this->_site );
fs_migrate_251( $this, $install_by_blog_id );
* @author Leo Fajardo (@leorw)
* @param \FS_Storage $storage
* @param bool|int|null $blog_id
private static function migrate_install_plan_to_plan_id( FS_Storage $storage, $blog_id = null ) {
if ( empty( $storage->sdk_version ) ) {
// New installation of the plugin, no need to upgrade.
if ( ! version_compare( $storage->sdk_version, '2.0.0', '<' ) ) {
// Previous version is >= 2.0.0, so no need to migrate.
$module_type = $storage->get_module_type();
$module_slug = $storage->get_module_slug();
$installs = self::get_all_sites( $module_type, $blog_id );
$install = isset( $installs[ $module_slug ] ) ? $installs[ $module_slug ] : null;
if ( ! is_object( $install ) ) {